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=""):
        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

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)

    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")

if __name__ == "__main__":
Copy the example files to a new directory
Run $AMSBIN/amspython generate_parameter_interface.py.
Verify that the correct absolute path to calculator.py is given in job_collection_engines.yaml


You need to specify the absolute path to calculator.py on the machine where ParAMS runs. If you run on a remote machine, make sure that the path is correct for that machine!


You cannot create new parameters in the GUI. The initial parameters must be created in a Python script as above.

5.7.3. Run the custom ASE parametrization

If you ran generate_parameter_interface.py so that the path to calculator.py is correctly given, then you can run the parametrization as normal.

  • Open job_collection.yaml in the ParAMS GUI, save the job and run it, or

  • run $AMSBIN/params from the command-line.

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...