#!/usr/bin/env amspython
# coding: utf-8
# ## Task, Engine and Property Blocks
#
#
from scm.plams import Settings, AMSJob, init
# this line is not required in AMS2025+
init()
# To start with, in order to see the input that will be passed to AMS from the Settings, make a function to create an AMSJob and print the input:
def print_input(settings):
print(AMSJob(settings=settings).get_input())
# Tasks can be specified in the settings under the `input.AMS.Task` key:
go_settings = Settings()
go_settings.input.AMS.Task = "GeometryOptimization"
go_settings.input.AMS.GeometryOptimization.Convergence.Gradients = 1e-5
print_input(go_settings)
# Properties can be specified under the `input.AMS.Properties` key:
nm_settings = Settings()
nm_settings.input.ams.Properties.NormalModes = "Yes"
print_input(nm_settings)
# Engine settings can be specified under the `input.AMS.<Engine>` key, for the engine of interest:
lda_settings = Settings()
lda_settings.input.ADF.Basis.Type = "DZP"
print_input(lda_settings)
# ## Combining Settings
# Settings objects can also be combined for easy reuse and job setup. Settings can be merged using the `+` and `+=` operators.
settings = go_settings + lda_settings + nm_settings
print_input(settings)
# Note however that this merge is a "soft" update, so values of existing keys will not be overwritten:
pbe_settings = Settings()
pbe_settings.input.ADF.Basis.Type = "TZP"
pbe_settings.input.ADF.xc.gga = "pbe"
settings += pbe_settings
print_input(settings)
# To achieve "hard update" behaviour, the `update` method can be used, which overwrites existing keys:
settings.update(pbe_settings)
print_input(settings)
# In AMS2025+, settings can also be removed using the `-` and `-=` operators:
def check_ams_version():
try:
from scm.plams import __version__
return __version__ >= "2024.2"
except ImportError:
return False
is_ams_2025_or_higher = check_ams_version()
if is_ams_2025_or_higher:
settings -= nm_settings
print_input(settings)
# Multiple values in a settings block can be configured using a list:
hybrid_settings = go_settings.copy()
hybrid_settings.input.AMS.Hybrid.Energy.Term = []
for i in range(5):
factor = (-1) ** (i % 2) * 1.0
region = "*" if i == 0 else "one" if i < 3 else "two"
engine_id = "adf-lda" if i == 0 or factor == -1 else "adf-gga"
term = Settings({"Factor": factor, "Region": region, "EngineID": engine_id})
hybrid_settings.input.AMS.Hybrid.Energy.Term.append(term)
hybrid_settings.input.AMS.Hybrid.Engine = [lda_settings.input.ADF.copy(), pbe_settings.input.ADF.copy()]
hybrid_settings.input.AMS.Hybrid.Engine[0]._h = "ADF adf-lda"
hybrid_settings.input.AMS.Hybrid.Engine[1]._h = "ADF adf-gga"
print_input(hybrid_settings)
# Note also in the example below, the use of the special `_h` "header" key, which can be used to add data to the header line for a block.
# ## Nested Keys
# It can be useful to access values from a Settings object using "nested" keys, available in AMS2025+. These are tuples of keys, where each successive element of the tuple corresponds to a further layer in the settings. Lists are flattened so their elements can be accessed with the corresponding index.
if is_ams_2025_or_higher:
print(list(hybrid_settings.nested_keys()))
if is_ams_2025_or_higher:
print(hybrid_settings.get_nested(("input", "AMS", "Task")))
if is_ams_2025_or_higher:
if hybrid_settings.contains_nested(("input", "AMS", "Hybrid", "Engine", 0)):
hybrid_settings.set_nested(("input", "AMS", "Hybrid", "Engine", 0, "Basis", "Type"), "TZP")
print(hybrid_settings.get_nested(("input", "AMS", "Hybrid", "Engine", 0, "Basis")))
# ## Comparison
# In AMS2025+, two settings objects can be compared to check the differences between them. The result will show the nested key and value of any added, removed and modified entries.
if is_ams_2025_or_higher:
import os
settings1 = go_settings + lda_settings + nm_settings
settings2 = go_settings.copy()
settings2.input.AMS.Task = "SinglePoint"
settings2.input.DFTB.Model = "GFN1-xTB"
comparison = settings2.compare(settings1)
print(
f"Items in settings2 not in settings1:{os.linesep}{os.linesep.join(f' - {k}: {v}' for k, v in comparison['added'].items())}"
)
print(
f"Items in settings1 not in settings2:{os.linesep}{os.linesep.join(f' - {k}: {v}' for k, v in comparison['removed'].items())}"
)
print(
f"Items modified from settings1 to settings2:{os.linesep}{os.linesep.join(f' - {k}: {v[1]} -> {v[0]}' for k, v in comparison['modified'].items())}"
)