Worked Example

Task, Engine and Property Blocks

from scm.plams import Settings, AMSJob, init

# this line is not required in AMS2025+
init()
PLAMS working folder: /path/plams/examples/AMSSettingsInput/plams_workdir

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)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Task GeometryOptimization

Properties can be specified under the input.AMS.Properties key:

nm_settings = Settings()
nm_settings.input.ams.Properties.NormalModes = "Yes"
print_input(nm_settings)
Properties
  NormalModes Yes
End

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)
Engine ADF
  Basis
    Type DZP
  End
EndEngine

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)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Properties
  NormalModes Yes
End

Task GeometryOptimization


Engine ADF
  Basis
    Type DZP
  End
EndEngine

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)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Properties
  NormalModes Yes
End

Task GeometryOptimization


Engine ADF
  Basis
    Type DZP
  End
  xc
    gga pbe
  End
EndEngine

To achieve “hard update” behaviour, the update method can be used, which overwrites existing keys:

settings.update(pbe_settings)
print_input(settings)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Properties
  NormalModes Yes
End

Task GeometryOptimization


Engine ADF
  Basis
    Type TZP
  End
  xc
    gga pbe
  End
EndEngine

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)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Properties
End

Task GeometryOptimization


Engine ADF
  Basis
    Type TZP
  End
  xc
    gga pbe
  End
EndEngine

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)
GeometryOptimization
  Convergence
    Gradients 1e-05
  End
End

Hybrid
  Energy
    Term
      EngineID adf-lda
      Factor 1.0
      Region *
    End
    Term
      EngineID adf-lda
      Factor -1.0
      Region one
    End
    Term
      EngineID adf-gga
      Factor 1.0
      Region one
    End
    Term
      EngineID adf-lda
      Factor -1.0
      Region two
    End
    Term
      EngineID adf-gga
      Factor 1.0
      Region two
    End
  End
  Engine ADF adf-lda
    Basis
      Type DZP
    End
  EndEngine
  Engine ADF adf-gga
    Basis
      Type TZP
    End
    xc
      gga pbe
    End
  EndEngine

End

Task GeometryOptimization

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()))
[('input',), ('input', 'AMS'), ('input', 'AMS', 'Task'), ('input', 'AMS', 'GeometryOptimization'), ('input', 'AMS', 'GeometryOptimization', 'Convergence'), ('input', 'AMS', 'GeometryOptimization', 'Convergence', 'Gradients'), ('input', 'AMS', 'Hybrid'), ('input', 'AMS', 'Hybrid', 'Energy'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 0), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 0, 'Factor'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 0, 'Region'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 0, 'EngineID'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 1), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 1, 'Factor'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 1, 'Region'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 1, 'EngineID'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 2), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 2, 'Factor'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 2, 'Region'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 2, 'EngineID'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 3), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 3, 'Factor'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 3, 'Region'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 3, 'EngineID'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 4), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 4, 'Factor'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 4, 'Region'), ('input', 'AMS', 'Hybrid', 'Energy', 'Term', 4, 'EngineID'), ('input', 'AMS', 'Hybrid', 'Engine'), ('input', 'AMS', 'Hybrid', 'Engine', 0), ('input', 'AMS', 'Hybrid', 'Engine', 0, '_h'), ('input', 'AMS', 'Hybrid', 'Engine', 0, 'Basis'), ('input', 'AMS', 'Hybrid', 'Engine', 0, 'Basis', 'Type'), ('input', 'AMS', 'Hybrid', 'Engine', 1), ('input', 'AMS', 'Hybrid', 'Engine', 1, '_h'), ('input', 'AMS', 'Hybrid', 'Engine', 1, 'Basis'), ('input', 'AMS', 'Hybrid', 'Engine', 1, 'Basis', 'Type'), ('input', 'AMS', 'Hybrid', 'Engine', 1, 'xc'), ('input', 'AMS', 'Hybrid', 'Engine', 1, 'xc', 'gga')]
if is_ams_2025_or_higher:
    print(hybrid_settings.get_nested(("input", "AMS", "Task")))
GeometryOptimization
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")))
Type:   TZP

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())}"
    )
Items in settings2 not in settings1:
  - ('input', 'DFTB', 'Model'): GFN1-xTB
Items in settings1 not in settings2:
  - ('input', 'ADF', 'Basis', 'Type'): DZP
  - ('input', 'AMS', 'Properties', 'NormalModes'): Yes
Items modified from settings1 to settings2:
  - ('input', 'AMS', 'Task'): GeometryOptimization -> SinglePoint