Skip to content

Engine

MD / simulation engine abstractions for LAMMPS, CP2K, and OpenMM.

Quick reference

Symbol Summary Preferred for
LAMMPSEngine LAMMPS simulation management Running LAMMPS simulations
CP2KEngine CP2K simulation management Running CP2K simulations
OpenMMEngine OpenMM simulation management Running OpenMM simulations
OpenMMSimulationConfig OpenMM run configuration Configuring an OpenMM run

Full API

Base

base

Engine base classes for molecular simulation engines.

Provides :class:Engine, an abstract base for running external computational chemistry programs (LAMMPS, CP2K, OpenMM, …). Each concrete engine handles command construction, file management, and subprocess execution for its specific program.

The two supported usage modes are:

  1. Generate-only — write input files to disk without executing anything::

    paths = engine.generate_inputs(frame, ff, config, "./output")

  2. Execute — write files and run the engine subprocess::

    result = engine.run(script, workdir="./calc")

MPI and job-scheduler launchers are supported via the launcher parameter::

engine = LAMMPSEngine("lmp", launcher=["mpirun", "-np", "16"])
engine = LAMMPSEngine("lmp", launcher=["srun", "--ntasks", "16"])

Engine

Engine(
    executable,
    *,
    workdir=None,
    launcher=None,
    env_vars=None,
    env=None,
    env_manager=None,
    check_executable=True,
)

Bases: ABC

Abstract base class for computational chemistry engines.

Concrete subclasses implement :meth:_execute and :meth:_get_default_extension. The base class handles script normalization, working-directory management, and command prefixing (launcher + environment wrapper).

Attributes:

Name Type Description
executable

Path or command to the engine binary.

work_dir

Default working directory; None means a temporary directory is created on each :meth:run call.

launcher

Optional MPI / scheduler prefix inserted before the executable, e.g. ["mpirun", "-np", "16"] or ["srun", "--ntasks", "16"].

env_vars dict[str, str]

Extra environment variables forwarded to the subprocess.

env

Conda / virtual-environment name to activate before execution.

env_manager

Environment manager; currently "conda" is supported.

scripts list[Script]

Scripts registered by the last :meth:run call (or [] before the first call).

input_script Script | None

Primary input script resolved by the last :meth:run call (or None before the first call).

Example

from molpy.core.script import Script from molpy.engine import LAMMPSEngine

script = Script.from_text( ... name="input", ... text="units real\natom_style full\n", ... language="other", ... ) engine = LAMMPSEngine(executable="lmp", check_executable=False) result = engine.run(script, workdir="./calc", check=False) print(result.returncode) 0

Initialise the engine.

Parameters:

Name Type Description Default
executable str

Path or command to the engine binary (e.g. "lmp").

required
workdir str | Path | None

Default working directory. None creates a temporary directory on each :meth:run call.

None
launcher list[str] | None

MPI or scheduler prefix prepended before the executable, e.g. ["mpirun", "-np", "16"] or ["srun", "--ntasks", "8"].

None
env_vars dict[str, str] | None

Extra environment variables set for the subprocess.

None
env str | None

Conda / virtual-environment name to activate. Must be provided together with env_manager.

None
env_manager str | None

Environment manager type. "conda" is currently supported; activation uses conda run -n <env>.

None
check_executable bool

Verify the executable is on PATH at construction time. Set to False in tests or when the binary is only available on a remote node.

True

Raises:

Type Description
FileNotFoundError

If check_executable is True and the executable is not found.

ValueError

If exactly one of env / env_manager is provided.

name abstractmethod property
name

Human-readable engine name (e.g. "LAMMPS").

Returns:

Type Description
str

A short, stable identifier used for logging and __repr__.

check_executable
check_executable()

Verify the executable is available on PATH.

Raises:

Type Description
FileNotFoundError

If the executable cannot be found.

run
run(
    scripts=None,
    *,
    workdir=None,
    capture_output=False,
    check=True,
    timeout=None,
    **kwargs,
)

Write scripts to disk and execute the engine.

Accepts scripts as :class:~molpy.core.script.Script objects, raw strings, :class:~pathlib.Path objects, or a list thereof. If workdir is given it is used for this call only — self.work_dir is not modified.

Parameters:

Name Type Description Default
scripts Script | str | Path | Sequence[Script] | None

Input script(s) to run. If None, previously registered scripts (from the last call) are re-used.

None
workdir str | Path | None

Working directory for this run. Overrides self.work_dir for the duration of the call only.

None
capture_output bool

Capture stdout/stderr.

False
check bool

Raise on non-zero exit code.

True
timeout float | None

Timeout in seconds.

None
**kwargs Any

Forwarded to :meth:_execute.

{}

Returns:

Type Description
CompletedProcess

class:subprocess.CompletedProcess with execution results.

Raises:

Type Description
ValueError

If no scripts are provided and none were registered previously.

CP2K

cp2k

CP2K quantum chemistry / molecular dynamics engine.

Wraps the CP2K <https://www.cp2k.org>_ program. The engine writes an input script to the working directory and runs::

[launcher...] cp2k.psmp -i <input> -o cp2k.out

Standard CP2K output (log) is redirected to cp2k.out via the -o flag; stdout is therefore empty, which avoids pipe-buffer deadlocks when the caller captures output.

MPI and scheduler launchers are configured on the :class:~molpy.engine.base.Engine base class::

engine = CP2KEngine("cp2k.psmp", launcher=["mpirun", "-np", "32"])
engine = CP2KEngine("cp2k.psmp", launcher=["srun", "--ntasks=32"])
Reference

Kühne, T. D. et al. (2020). CP2K: An electronic structure and molecular dynamics software package. J. Chem. Phys. 152, 194103. https://doi.org/10.1063/5.0007045

CP2KEngine

CP2KEngine(
    executable,
    *,
    workdir=None,
    launcher=None,
    env_vars=None,
    env=None,
    env_manager=None,
    check_executable=True,
)

Bases: Engine

CP2K quantum chemistry / molecular dynamics engine.

Runs CP2K input scripts. The typical executable name is cp2k.psmp (MPI + OpenMP build) or cp2k.popt (MPI only).

A minimal CP2K input must contain at least &GLOBAL, &FORCE_EVAL, and &MOTION (or &ENERGY) sections.

Example

from molpy.core.script import Script from molpy.engine import CP2KEngine

inp = ( ... "&GLOBAL\n" ... " PROJECT water\n" ... " RUN_TYPE ENERGY\n" ... "&END GLOBAL\n" ... "&FORCE_EVAL\n" ... " METHOD Quickstep\n" ... "&END FORCE_EVAL\n" ... ) script = Script.from_text(name="input", text=inp, language="other") engine = CP2KEngine(executable="cp2k.psmp", check_executable=False) result = engine.run(script, workdir="./calc", check=False) print(result.returncode) 0

MPI execution::

engine = CP2KEngine("cp2k.psmp", launcher=["mpirun", "-np", "32"])
result = engine.run(script, workdir="./calc")
name property
name

Return "CP2K".

Returns:

Type Description
str

Engine identifier string.

LAMMPS

lammps

LAMMPS molecular dynamics engine.

Wraps the LAMMPS <https://www.lammps.org>_ molecular dynamics code. The engine writes an input script to the working directory and runs::

[launcher...] lmp -in <input> -log log.lammps -screen none

The -screen none flag suppresses duplicate stdout output; all per-timestep data is written exclusively to log.lammps.

MPI and scheduler launchers are configured on the :class:~molpy.engine.base.Engine base class::

engine = LAMMPSEngine("lmp", launcher=["mpirun", "-np", "16"])
engine = LAMMPSEngine("lmp", launcher=["srun", "--ntasks=16"])
Reference

Thompson, A. P. et al. (2022). LAMMPS — A flexible simulation tool for particle-based materials modeling. Comput. Phys. Commun. 271, 108171. https://doi.org/10.1016/j.cpc.2021.108171

LAMMPSEngine

LAMMPSEngine(
    executable=None, *, check_executable=True, **kwargs
)

Bases: Engine

LAMMPS molecular dynamics engine.

Runs LAMMPS input scripts. The engine binary is typically named lmp, lmp_serial, or lmp_mpi depending on the build.

Example

from molpy.core.script import Script from molpy.engine import LAMMPSEngine

script = Script.from_text( ... name="input", ... text="units real\natom_style full\nrun 0\n", ... language="other", ... ) engine = LAMMPSEngine(executable="lmp", check_executable=False) result = engine.run(script, workdir="./calc", check=False) print(result.returncode) 0

MPI execution::

engine = LAMMPSEngine("lmp", launcher=["mpirun", "-np", "16"])
result = engine.run(script, workdir="./calc")

Initialise the LAMMPS engine.

Differs from :class:~molpy.engine.base.Engine only in that executable is optional: when omitted, the first binary found on PATH among lmp, lmp_serial, lmp_mpi is used, so LAMMPSEngine() works out of the box on a typical install.

Parameters:

Name Type Description Default
executable str | None

Path or command to the LAMMPS binary. None auto-detects (see above).

None
check_executable bool

Verify the resolved executable is on PATH.

True
**kwargs Any

Forwarded to :class:~molpy.engine.base.Engine (workdir, launcher, env_vars, env, env_manager).

{}
name property
name

Return "LAMMPS".

Returns:

Type Description
str

Engine identifier string.

md
md(
    frame,
    ff,
    *,
    ensemble="nve",
    steps=1000,
    temperature=300.0,
    timestep=1.0,
    seed=12345,
    limit=0.1,
    pair_style="lj/cut/coul/cut 10.0",
    atom_style="full",
    units="real",
    workdir=None,
    capture_output=False,
    timeout=None,
)

Run short MD on frame under ff and return a new frame.

A thin sibling of :meth:minimize for settling a packed box. Note that a freshly packed box carries residual clashes; run :meth:minimize first, or use ensemble="nve/limit", to avoid a blow-up under plain nve.

Parameters:

Name Type Description Default
frame Frame

Input structure; must carry a periodic box (frame.box).

required
ff ForceField

Typified force field.

required
ensemble str

One of "nve", "nve/limit", "nvt".

'nve'
steps int

Number of MD steps.

1000
temperature float

Initial / target temperature (K).

300.0
timestep float

Timestep in units time (fs for real).

1.0
seed int

RNG seed for the initial velocity distribution.

12345
limit float

Per-step displacement cap (Å) for ensemble="nve/limit".

0.1
pair_style str

LAMMPS pair_style line.

'lj/cut/coul/cut 10.0'
atom_style str

LAMMPS atom_style.

'full'
units str

LAMMPS units.

'real'
workdir str | Path | None

Working directory; temporary when None.

None
capture_output bool

Capture LAMMPS stdout/stderr.

False
timeout float | None

Subprocess timeout in seconds.

None

Returns:

Type Description
Frame

A new :class:~molrs.Frame with the post-MD coordinates.

Raises:

Type Description
ValueError

If ensemble is unknown or frame has no box.

minimize
minimize(
    frame,
    ff,
    *,
    etol=0.0001,
    ftol=1e-06,
    max_iter=1000,
    max_eval=10000,
    pair_style="lj/cut/coul/cut 10.0",
    atom_style="full",
    units="real",
    workdir=None,
    capture_output=False,
    timeout=None,
)

Energy-minimise frame under force field ff and return a new frame.

Writes a LAMMPS data file and coefficient settings from frame / ff, runs minimize, then splices the relaxed coordinates back onto a copy of frame (topology, types, and box preserved). frame is not mutated.

Typical use is removing residual overlaps after packing::

eng = LAMMPSEngine()
relaxed = eng.minimize(pack_result.frame, ff)

Parameters:

Name Type Description Default
frame Frame

Input structure; must carry a periodic box (frame.box).

required
ff ForceField

Typified force field providing pair/bond/angle/... coefficients.

required
etol float

Energy stopping tolerance (unitless).

0.0001
ftol float

Force stopping tolerance (force units).

1e-06
max_iter int

Maximum minimiser iterations.

1000
max_eval int

Maximum force/energy evaluations.

10000
pair_style str

LAMMPS pair_style line for minimisation. The default lj/cut/coul/cut avoids a long-range solver; switch to lj/cut/coul/long (with a kspace_style) for production MD.

'lj/cut/coul/cut 10.0'
atom_style str

LAMMPS atom_style (full by default).

'full'
units str

LAMMPS units (real by default).

'real'
workdir str | Path | None

Directory for input/output files; a temporary directory is created when None.

None
capture_output bool

Capture LAMMPS stdout/stderr.

False
timeout float | None

Subprocess timeout in seconds.

None

Returns:

Type Description
Frame

A new :class:~molrs.Frame with relaxed coordinates.

Raises:

Type Description
ValueError

If frame has no box.

CalledProcessError

If LAMMPS exits non-zero.

RuntimeError

If LAMMPS produces no output structure.

OpenMM

openmm

OpenMM simulation engine for MolPy.

Generates OpenMM input files (PDB + XML force field + Python simulation script) from :class:molrs.Frame and :class:~molpy.core.forcefield.ForceField objects. OpenMM itself is not required for input generation; it is only needed for :meth:~OpenMMEngine.serialize_system.

Two usage modes are supported:

  1. Generate only (no OpenMM required)::

    config = OpenMMSimulationConfig(ensemble="NVT", n_steps=50_000) engine = OpenMMEngine(check_executable=False) paths = engine.generate_inputs(frame, ff, config, "./output") # Hand the files to any HPC scheduler.

  2. Generate and run (OpenMM must be importable)::

    result = engine.run(paths["script"], workdir="./output")

Reference

Eastman, P. et al. (2017). OpenMM 7: Rapid development of high performance algorithms for molecular dynamics. PLOS Comput. Biol. 13(7), e1005659. https://doi.org/10.1371/journal.pcbi.1005659

OpenMMEngine

OpenMMEngine(
    executable="python",
    *,
    workdir=None,
    launcher=None,
    env_vars=None,
    env=None,
    env_manager=None,
    check_executable=True,
)

Bases: Engine

OpenMM molecular dynamics engine.

Generates a complete set of OpenMM input files from MolPy :class:molrs.Frame and :class:~molpy.core.forcefield.ForceField objects. OpenMM itself is not required for input generation; it is only needed for :meth:serialize_system.

The generated Python script can be executed directly::

python simulate.py

or via the engine::

result = engine.run(paths["script"], workdir="./output")
Example

from molpy.engine import OpenMMEngine, OpenMMSimulationConfig

config = OpenMMSimulationConfig(ensemble="NVT", n_steps=10_000) engine = OpenMMEngine(check_executable=False) paths = engine.generate_inputs(frame, ff, config, "./output")

paths["pdb"], paths["forcefield"], paths["script"]

Initialise the OpenMM engine.

Parameters:

Name Type Description Default
executable str

Python interpreter used when :meth:run executes the generated simulation script. Defaults to "python".

'python'
workdir str | Path | None

Default working directory for :meth:run.

None
launcher list[str] | None

MPI / scheduler prefix, e.g. ["mpirun", "-np", "4"]. Prepended before executable when running the script.

None
env_vars dict[str, str] | None

Extra environment variables forwarded to the subprocess.

None
env str | None

Conda / virtual-environment name to activate.

None
env_manager str | None

Environment manager ("conda" supported).

None
check_executable bool

Verify executable is on PATH at construction. Set False when only using :meth:generate_inputs.

True
name property
name

Return "OpenMM".

Returns:

Type Description
str

Engine identifier string.

generate_inputs
generate_inputs(
    frame,
    forcefield,
    config,
    output_dir,
    *,
    pdb_filename="system.pdb",
    ff_filename="forcefield.xml",
    script_filename="simulate.py",
)

Generate PDB, XML force field, and Python simulation script.

Does not require OpenMM to be installed.

Parameters:

Name Type Description Default
frame 'Frame'

:class:molrs.Frame with atom positions.

required
forcefield 'ForceField'

MolPy :class:~molpy.core.forcefield.ForceField containing interaction parameters.

required
config OpenMMSimulationConfig

Simulation parameters.

required
output_dir PathLike

Directory where files are written (created if absent).

required
pdb_filename str

Name for the PDB coordinate file.

'system.pdb'
ff_filename str

Name for the XML force field file.

'forcefield.xml'
script_filename str

Name for the Python simulation script.

'simulate.py'

Returns:

Type Description
dict[str, Path]

Dictionary with keys "pdb", "forcefield", and "script"

dict[str, Path]

mapping to :class:~pathlib.Path objects.

serialize_system
serialize_system(
    frame,
    forcefield,
    config,
    output_dir,
    *,
    pdb_filename="system.pdb",
    ff_filename="forcefield.xml",
    script_filename="simulate.py",
    system_xml_filename="system.xml",
    integrator_xml_filename="integrator.xml",
)

Generate inputs and serialise the OpenMM System + Integrator to XML.

Calls :meth:generate_inputs first, then builds OpenMM objects from those files and serialises them with openmm.XmlSerializer. The resulting system.xml and integrator.xml files can be loaded back without re-parsing the force field.

Parameters:

Name Type Description Default
frame 'Frame'

:class:molrs.Frame with atom positions.

required
forcefield 'ForceField'

MolPy :class:~molpy.core.forcefield.ForceField.

required
config OpenMMSimulationConfig

Simulation parameters.

required
output_dir PathLike

Output directory.

required
pdb_filename str

PDB coordinate file name.

'system.pdb'
ff_filename str

XML force field file name.

'forcefield.xml'
script_filename str

Python simulation script name.

'simulate.py'
system_xml_filename str

Serialised System XML file name.

'system.xml'
integrator_xml_filename str

Serialised Integrator XML file name.

'integrator.xml'

Returns:

Type Description
dict[str, Path]

Dictionary extending :meth:generate_inputs output with keys

dict[str, Path]

"system_xml" and "integrator_xml".

Raises:

Type Description
ImportError

If OpenMM is not installed.

OpenMMSimulationConfig dataclass

OpenMMSimulationConfig(
    ensemble="NVT",
    temperature=300.0,
    pressure=1.0,
    timestep_fs=2.0,
    n_steps=500000,
    nonbonded_method="PME",
    nonbonded_cutoff_nm=1.0,
    constraints="HBonds",
    friction_per_ps=1.0,
    dcd_reporter_interval=1000,
    state_reporter_interval=1000,
    checkpoint_interval=10000,
    output_dcd="trajectory.dcd",
    output_log="simulation.log",
    output_chk="checkpoint.chk",
    minimize_tolerance=10.0,
    minimize_max_iterations=1000,
    barostat_frequency=25,
    platform="CUDA",
)

Configuration for an OpenMM simulation.

All fields have physical units encoded in their names. The object round-trips through JSON via :meth:to_json / :meth:from_json.

Attributes:

Name Type Description
ensemble Literal['NVT', 'NPT', 'NVE', 'minimize']

Simulation ensemble — "NVT", "NPT", "NVE", or "minimize".

temperature float

Temperature in Kelvin.

pressure float

Pressure in bar (NPT only).

timestep_fs float

Integration timestep in femtoseconds.

n_steps int

Number of MD steps.

nonbonded_method Literal['NoCutoff', 'CutoffNonPeriodic', 'CutoffPeriodic', 'PME', 'EWALD']

Nonbonded treatment. Must be a valid openmm.app attribute name ("NoCutoff", "CutoffNonPeriodic", "CutoffPeriodic", "PME", "EWALD").

nonbonded_cutoff_nm float

Nonbonded cutoff radius in nanometres.

constraints Literal['None', 'HBonds', 'AllBonds', 'HAngles']

Constraint scheme — a valid openmm.app attribute name or "None" for no constraints.

friction_per_ps float

Langevin friction coefficient in ps⁻¹.

dcd_reporter_interval int

Steps between DCD trajectory frames.

state_reporter_interval int

Steps between log lines.

checkpoint_interval int

Steps between checkpoint saves.

output_dcd str

Filename for the DCD trajectory.

output_log str

Filename for the state-data log.

output_chk str

Filename for checkpoint files.

minimize_tolerance float

Energy minimisation tolerance in kJ mol⁻¹ nm⁻¹.

minimize_max_iterations int

Maximum minimisation iterations.

barostat_frequency int

MC barostat move frequency in steps (NPT only).

platform str

OpenMM platform name ("CUDA", "OpenCL", "CPU").

from_dict classmethod
from_dict(data)

Construct a config from a plain dictionary.

Parameters:

Name Type Description Default
data dict[str, Any]

Dict as returned by :meth:to_dict.

required

Returns:

Name Type Description
New 'OpenMMSimulationConfig'

class:OpenMMSimulationConfig instance.

from_json classmethod
from_json(path)

Load a configuration from a JSON file.

Parameters:

Name Type Description Default
path PathLike

Path to a JSON file previously written by :meth:to_json.

required

Returns:

Name Type Description
New 'OpenMMSimulationConfig'

class:OpenMMSimulationConfig instance.

Raises:

Type Description
FileNotFoundError

If path does not exist.

to_dict
to_dict()

Return a JSON-serialisable dictionary of all fields.

Returns:

Type Description
dict[str, Any]

Plain dict with field names as keys and Python scalars as values.

to_json
to_json(path)

Write the configuration to a JSON file.

Parameters:

Name Type Description Default
path PathLike

Destination file path (created or overwritten).

required

Raises:

Type Description
OSError

If the file cannot be written.