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 theParAMS
ExtraEngineID,modifies
job_collection_engines.yaml
so that theParAMS
engine has the absolute path tocalculator.py
specified inASE%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()
$AMSBIN/amspython generate_parameter_interface.py
.calculator.py
is given in job_collection_engines.yaml
Note
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!
Note
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...