Skip to content

Molecular Dynamics

The run() method on calculators that implement BaseMDCalculator performs molecular dynamics using ASE's integrators. The supported ensembles cover NVE, six NVT thermostats (Nosé-Hoover, Langevin, Andersen, Bussi, Berendsen, and a Nosé-Hoover chain), and six NPT/barostat variants (Nosé-Hoover, isotropic MTK, MTK, masked MTK, Berendsen, and inhomogeneous Berendsen).

Basic NVT Example

Every keyword has a default, including ensemble ("nve") and temperature (300 K), so the example below only overrides what changes for this run.

from ase.build import bulk
from materialsframework.calculators import GraceCalculator

struct = bulk("Cu", "fcc", a=3.6, cubic=True)

calc = GraceCalculator(
    ensemble="nvt_nose_hoover",
    temperature=300,   # K
    verbose=True,
)
res = calc.run(structure=struct, steps=1000)

print(res["final_structure"])
print(res["temperature"])
print(res["total_energy"])

Ensembles

Pass one of the keywords below as ensemble when constructing the calculator.

Ensemble keyword Description
"nve" Microcanonical (constant N, V, E)
"nvt_nose_hoover" Canonical via Nosé-Hoover thermostat
"langevin" Canonical via Langevin (friction + stochastic force) thermostat
"andersen" Canonical via Andersen stochastic collision thermostat
"bussi" Canonical via Bussi stochastic velocity rescaling
"nvt_berendsen" Canonical via Berendsen velocity scaling
"nose_hoover_chain_nvt" Canonical via a modern Nosé-Hoover chain thermostat
"npt_nose_hoover" Isothermal-isobaric via Nosé-Hoover
"isotropic_mtk_npt" Isothermal-isobaric via Martyna-Tobias-Klein (MTK), isotropic volume fluctuations only
"mtk_npt" Isothermal-isobaric via MTK, full anisotropic cell fluctuations
"masked_mtk_npt" Isothermal-isobaric via MTK, cell fluctuations restricted to the axes set in mask
"npt_berendsen" NPT with Berendsen barostat
"inhomogeneous_npt_berendsen" Inhomogeneous NPT Berendsen

NPT ensembles also accept pressure (defaults to 1 atm):

# NPT example
calc = GraceCalculator(
    ensemble="npt_nose_hoover",
    temperature=1000,  # K
    pressure=0.0,      # atm
)
res = calc.run(structure=struct, steps=5000)

Output Dictionary

run() records one entry per recorded step for every array-valued key below, at the interval set by interval.

Key Description
final_structure Final structure as pymatgen Structure
total_energy Array of total energies per step (eV)
potential_energy Array of potential energies per step (eV)
kinetic_energy Array of kinetic energies per step (eV)
forces Array of atomic forces per step (eV/A)
stresses Array of stress tensors per step
temperature Array of temperatures per step (K)
velocities Array of atomic velocities per step

Timestep and Logging

timestep sets the simulated time per step, in femtoseconds. loginterval controls how often output is written to logfile, which defaults to None and disables logging. Set logfile to a path and ASE's MDLogger writes step, time, energy, temperature, and stress to that file every loginterval steps.

calc = GraceCalculator(
    ensemble="nvt_nose_hoover",
    temperature=300,
    timestep=2.0,       # fs
    loginterval=10,     # write the log every 10 steps
    interval=10,        # record trajectory data every 10 steps
    logfile="md.log",
)
res = calc.run(structure=struct, steps=10000)

Velocity Initialization

If the input structure has no velocities set, initial velocities are drawn from a Maxwell-Boltzmann distribution at the target temperature. If you pass an ase.Atoms with velocities already set via set_velocities()/set_momenta(), those are used as-is instead. This only applies to ase.Atoms input: pymatgen Structure/Molecule carry no velocity information, and neither does run()'s own final_structure output, so re-running on either always reinitializes velocities.