Source code for atomate2.forcefields.flows.eos
"""Flows to generate EOS fits using machine learned interatomic potentials."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING
from atomate2.common.flows.eos import CommonEosMaker
from atomate2.forcefields.jobs import ForceFieldRelaxMaker
if TYPE_CHECKING:
from jobflow import Maker
from typing_extensions import Self
from atomate2.forcefields import MLFF
[docs]
@dataclass
class ForceFieldEosMaker(CommonEosMaker):
"""
Generate equation of state data using an ML forcefield.
First relax a structure using relax_maker.
Then perform a series of deformations on the relaxed structure, and
relax atomic positions within the cell. For ML forcefields, there
is no distinction between relax and static energies, unlike in a VASP
calculation. Therefore these EosMakers default to static_maker = None.
Parameters
----------
name : str
Name of the flows produced by this maker.
initial_relax_maker : .Maker | None
Maker to relax the input structure, defaults to None (no initial relaxation).
eos_relax_maker : .Maker
Maker to relax deformed structures for the EOS fit.
static_maker : .Maker | None
Maker to generate statics after each relaxation, defaults to None.
strain : tuple[float]
Percentage linear strain to apply as a deformation, default = -5% to 5%.
number_of_frames : int
Number of strain calculations to do for EOS fit, default = 6.
postprocessor : .job
Optional postprocessing step, defaults to
`atomate2.common.jobs.PostProcessEosEnergy`.
_store_transformation_information : .bool = False
Whether to store the information about transformations. Unfortunately
needed at present to handle issues with emmet and pydantic validation
TODO: remove this when clash is fixed
"""
name: str = "Forcefield EOS Maker"
initial_relax_maker: Maker = field(default_factory=ForceFieldRelaxMaker)
eos_relax_maker: Maker = field(
default_factory=lambda: ForceFieldRelaxMaker(relax_cell=False)
)
static_maker: Maker = None
[docs]
@classmethod
def from_force_field_name(
cls,
force_field_name: str | MLFF | dict,
calculator_kwargs: dict | None = None,
relax_initial_structure: bool = True,
**kwargs,
) -> Self:
"""
Create an EOS flow from a forcefield name.
Parameters
----------
force_field_name : str or .MLFF or dict
The name of the force field.
calculator_kwargs : dict | None
The keyword arguments to pass to the calculator
relax_initial_structure: bool = True
Whether to relax the initial structure before performing an EOS fit.
**kwargs
Additional kwargs to pass to ForceFieldEosMaker
Returns
-------
ForceFieldEosMaker
"""
calculator_kwargs = calculator_kwargs or {}
eos_relax_maker = ForceFieldRelaxMaker(
force_field_name=force_field_name,
calculator_kwargs=calculator_kwargs,
relax_cell=False,
)
kwargs.update(
initial_relax_maker=(
ForceFieldRelaxMaker(
force_field_name=force_field_name,
calculator_kwargs=calculator_kwargs,
)
if relax_initial_structure
else None
),
eos_relax_maker=eos_relax_maker,
static_maker=None,
)
return cls(
name=f"{eos_relax_maker.mlff.name} EOS Maker",
**kwargs,
)