Skip to content

ASE workflows

ASE-based tools (ASE's optimizers, NEB, molecular dynamics, Sella run by hand) get energies and forces from an ASE calculator. qccodec.ase.ProgramCalculator is one whose every request is a calculation of any qccodec program: give it a ProgramSpec and a function that computes a ProgramInput, such as qccompute.compute. The program runs through its codec, so its results, failures, and provenance are the same as anywhere else in qc*.

from ase.io import read
from ase.optimize import BFGS
from qccompute import compute
from qcdata import CalcType, Model, ProgramSpec

from qccodec.ase import ProgramCalculator

spec = ProgramSpec(program="xtb", calctype=CalcType.gradient, model=Model(method="gfn2"))
atoms = read("water.xyz")
atoms.calc = ProgramCalculator(spec, compute, charge=0, multiplicity=1)
BFGS(atoms).run(fmax=0.01)

print(atoms.get_potential_energy())  # eV
print(len(atoms.calc.outputs), "calculations")  # every ProgramOutput, in order
  • qccodec never runs programs, so you pass compute. Any function taking a ProgramInput and returning a ProgramOutput works.
  • Units are converted at the boundary: positions in angstrom, energies in eV, forces in eV/angstrom, as ASE expects.
  • Charge and multiplicity come from charge and multiplicity, or from a structure= whose charge, multiplicity, and identifiers every calculation keeps.
  • A failed calculation raises ASE's CalculationFailed, with the ProgramOutput as its .output. With qccompute.compute's default raise_exc=True, qccompute's own exception is raised first.
  • Each request is one calculation, so ASE workflows do not get qccompute's batching or parallel lists. For optimizations and transition states, the sella and geomeTRIC codecs run the whole optimization as one calculation instead. Requires ASE (pip install ase; qccodec[sella] includes it).

qccodec.ase.ProgramCalculator(spec: ProgramSpec, compute: Compute, *, charge: int = 0, multiplicity: int = 1, structure: Structure | None = None, structures: dict[str, Structure] | None = None, **kwargs: Any)

An ASE calculator running a qccodec program for every energy and force.

Attributes:

Name Type Description
outputs list[ProgramOutput[ProgramInput, Data]]

Every output computed, in order, including a failed one.

Parameters:

Name Type Description Default
spec ProgramSpec

The program, model, and keywords; calctype gradient gives energies and forces, energy only energies.

required
compute Compute

Runs one input and returns its output, such as qccompute.compute.

required
charge int

Total charge, when no structure is given.

0
multiplicity int

Spin multiplicity, when no structure is given.

1
structure Structure | None

A structure whose charge, multiplicity, and identifiers every calculation keeps; its elements must match the atoms'.

None
structures dict[str, Structure] | None

Named structures for the program, such as QM/MM environments.

None
**kwargs Any

Passed to ase.calculators.calculator.Calculator.

{}

Raises:

Type Description
ValueError

spec is not an energy or gradient calculation.

Source code in src/qccodec/ase.py
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def __init__(
    self,
    spec: ProgramSpec,
    compute: Compute,
    *,
    charge: int = 0,
    multiplicity: int = 1,
    structure: Structure | None = None,
    structures: dict[str, Structure] | None = None,
    **kwargs: Any,
) -> None:
    """Create the calculator.

    Args:
        spec: The program, model, and keywords; ``calctype`` ``gradient`` gives
            energies and forces, ``energy`` only energies.
        compute: Runs one input and returns its output, such as
            ``qccompute.compute``.
        charge: Total charge, when no ``structure`` is given.
        multiplicity: Spin multiplicity, when no ``structure`` is given.
        structure: A structure whose charge, multiplicity, and identifiers every
            calculation keeps; its elements must match the atoms'.
        structures: Named structures for the program, such as QM/MM environments.
        **kwargs: Passed to ``ase.calculators.calculator.Calculator``.

    Raises:
        ValueError: ``spec`` is not an energy or gradient calculation.
    """
    if spec.calctype not in (CalcType.energy, CalcType.gradient):
        raise ValueError(
            f"ProgramCalculator needs an energy or gradient spec, not "
            f"{spec.calctype.value}."
        )
    super().__init__(**kwargs)
    self.spec = spec
    self.compute = compute
    self.charge = charge
    self.multiplicity = multiplicity
    self.template = structure
    self.structures = structures or {}
    self.outputs: list[ProgramOutput[ProgramInput, Data]] = []