# 5.7. ASE Calculator parametrization¶

This tutorial shows you how you can

• parametrize a custom ASE calculator with ParAMS

Starting with AMS2023.1, AMS can use any custom ASE calculator as the “engine”. If you define the ASE calculator to be able to read the ParAMS parameter_interface.yaml format, you can use ParAMS to optimize the parameters used by the ASE calculator.

The files for this tutorial are located in $AMSHOME/scripting/scm/params/examples/CustomASECalculator. Copy the files to a new empty directory. ## 5.7.1. Definition of the ASE calculator¶ The file calculator.py defines an ASE calculator called MyCustomASECalculator. The calculate() method should populate the energy (in eV) and ideally also the forces (in eV/ang) in self.results['energy'] and self.results['forces']. For more information about how to write ASE calculators, see the ASE website. In this example, the energy is calculated by means of pair-wise springs with spring constants k and r0. The function get_calculator(params_path) receives the path to the parameter_interface.yaml file and returns an instance of the ASE calculator. This function must be called get_calculator. #!/usr/bin/env amspython from ase.calculators.calculator import Calculator from scm.params import ASEParameters from scm.plams import * class MyCustomASECalculator(Calculator): implemented_properties = ["energy"] def __init__(self, params_path=""): Calculator.__init__(self) self.parameter_interface = ASEParameters.yaml_load(params_path) def calculate(self, atoms=None, properties=None, system_changes=None): distance_matrix = atoms.get_all_distances(mic=True) energy = 0 for i in range(len(atoms)): isym = atoms.symbols[i] energy += self.parameter_interface[f"ATM:{isym}:self_energy"].value for j in range(i): jsym = atoms.symbols[j] pname = f"{isym}.{jsym}" if isym < jsym else f"{jsym}.{isym}" k = self.parameter_interface[f"BND:{pname}:k"].value r0 = self.parameter_interface[f"BND:{pname}:r0"].value energy += 0.5 * k * (distance_matrix[i, j] - r0) ** 2 self.results["energy"] = energy # note: also populate self.results['forces'] for significant speedup, otherwise the forces are calculated numerically by the AMS Driver def get_calculator(params_path=""): """ params_path: str Path to a parameter_interface.yaml file in the ParAMS format for an interface of type ASEParameters. """ return MyCustomASECalculator(params_path=params_path)  ## 5.7.2. Creation of the initial parameter interface¶ The file generate_parameter_interface.py • creates the parameter_interface.yaml file, • modifies job_collection.yaml so that each job uses the ParAMS ExtraEngineID, • modifies job_collection_engines.yaml so that the ParAMS engine has the absolute path to calculator.py specified in ASE%File. Note that the parameter names in parameter_interface.yaml can be completely arbitrary, as long as the calculator in calculator.py can interpret them. #!/usr/bin/env amspython from scm.plams import Settings from scm.params import ASEParameters, EngineCollection, JobCollection, Engine from scm.params.parameterinterfaces.base import Parameter import os """ Copy all files to a new directory before running this file! It will overwrite parameter_interface.yaml, job_collection.yaml, and job_collection_engines.yaml In particular, the absolute path to calculator.py will be set inside job_collection_engines.yaml Run as$AMSBIN/amspython generate_parameter_interface.py
"""

def main():
parameters = [
Parameter(name="ATM:O:self_energy", value=-80.0, range=(-200, 200), is_active=False),
Parameter(name="ATM:H:self_energy", value=-20.0, range=(-200, 200), is_active=False),
Parameter(name="BND:H.H:k", value=10.0, range=(0, 100), is_active=True),
Parameter(name="BND:H.H:r0", value=1.5, range=(1.0, 3), is_active=True),
Parameter(name="BND:H.O:k", value=50.0, range=(0, 100), is_active=True),
Parameter(name="BND:H.O:r0", value=1.0, range=(0.5, 3), is_active=True),
Parameter(name="BND:O.O:k", value=10.0, range=(0, 100), is_active=False),
Parameter(name="BND:O.O:r0", value=3.0, range=(1, 8), is_active=False),
]

interf = ASEParameters(parameters=parameters)
interf.yaml_store("parameter_interface.yaml")

s = Settings()
s.input.ASE.Type = "File"

# calculator.py contains a function get_calculator(params_path, **kwargs)
# which returns the ASE calclator. You must set the absolute path to
# calculator.py inside job_collection_engines.yaml on the machine where you run
# params.

s.input.ASE.File = os.path.abspath("calculator.py")

# update the job collection so that
# * all entries have ExtraEngineID ParAMS
# * the ParAMS engine in job_collection_engines.yaml has the definition above

jc = JobCollection("job_collection.yaml")
jc.set_extra_engine(s)
jc.store("job_collection.yaml")

if __name__ == "__main__":
main()

Copy the example files to a new directory

## 5.7.4. Results of the custom ASE parametrization¶

The following figure can be produced by setting

• the first graph to Loss:loss

• the second graph to Bondscan...

• the third graph to Anglescan...