Skip to content

Simulation

Simulation

MD on the graph engine with a plan, an optional gate, schedules and complete restarts.

Simulation(atoms, force_field, *, species=None, integrator, workload=None, output=None, seed, check="skip", allow_naive_cueq=False, mode=None, allow_reference=False, fallback=None, gate=None, runner=None, engine=None, neighbours=None, adaptation=None, calibration=None):

  • atoms: ASE Atoms, full 3D PBC (§4.4); copied, the caller's object is never changed;
  • force_field: a fm.ForceField, an fm.load result or a label; a model is wrapped in a float32 ForceField with species= (default: the structure's species). A path raises TypeError (load it with fm.load, the trust decision needs one); species with a ForceField raises ValueError (the ForceField owns the superset);
  • integrator: an fm.integrators record (fm.integrators.RECORDS); a scheme not registered yet is refused by the plan (rule 1);
  • workload: fm.Workload; default Workload(max_temperature_K=integrator.temperature_K);
  • output: fm.Output; thermo rows, trajectory frames and metadata.json go to its directory, in chunks (§10.6). None writes no file;
  • seed: the integrator's noise seed, recorded;
  • check: one of CHECK_MODES;
  • allow_naive_cueq: accept cuEquivariance's naive kernels (§9.2), recorded;
  • the planner inputs (PlannerOptions, recorded in simulation.json): mode (§9.3; None lets the planner choose), allow_reference (rule 1), fallback (FallbackConfig, §9.4), gate (GateConfig, §12.3 "Resources"; the record is sim.gate_config, the result sim.gate), runner (RunnerConfig, §8.7), engine (EngineConfig) and neighbours (NeighbourConfig), adaptation (AdaptationConfig, §9.6) and calibration (CalibrationConfig, §9.1; both phase 5, plan task 5-T01). None takes the defaults, and for engine and neighbours the default planner's.

plan property

plan: Any

The active Plan revision (§9.5).

fallback property

fallback: FallbackConfig

The FallbackConfig this simulation was made with (§9.4).

gate_config property

gate_config: GateConfig

The GateConfig this simulation was made with (§12.3 "Resources"); gate is the result.

runner property

runner: RunnerConfig

The RunnerConfig this simulation was made with (§8.7).

gate property

gate: Any

The gate record (planner.selfcheck.GateRecord, §12.3): status passed, unavailable (with check="allow_unavailable") or skipped; a failed gate raised GateFailed at construction.

report property

report: Any

The run report (fastmace.report.Report, §7.7).

schedule property

schedule: Any

The attached schedule position (protocols.ScheduleState), or None.

degrees_of_freedom property

degrees_of_freedom: int

The degrees of freedom of this run's reported temperatures: 3N - 3 under the "mass_weighted" fixcm projection, 3(N - n_fixed) with FixAtoms, else 3N (io.trajectory.degrees_of_freedom; SPEC §10.4, decisions 37 and 45).

reference_volume_A3 property

reference_volume_A3: float | None

V0 of the cell-divergence stop: the NPT run's starting volume (ų), kept across a resume.

committed

committed() -> Committed

The committed step as read-only host arrays (flushes any step in flight).

submit_frame

submit_frame(
    sink: Callable[[int, Mapping[str, Any]], Any],
) -> None

Queue the committed step's output values for sink(step, values) (host arrays: positions, momenta, forces, energy, kinetic; cell and stress for NPT). The copies run behind the GPU; sink is called after the next step has been launched, or by flush_output.

add_sink

add_sink(
    sink: Callable[[int, Mapping[str, Any]], Any],
    interval: int,
) -> None

Call sink(step, values) for every step of run that is a multiple of interval, with the values of submit_frame, through the same asynchronous queue as the chunked output (for fastmace.ase's trajectory and log files). interval is independent of the Output cadences; the queue is drained, oldest first, at every restart point and at the end of each run, so a sink has seen every due step up to there. Step 0 is the caller's (submit_frame).

flush_output

flush_output() -> None

Write everything queued and flush the output files.

metadata

metadata() -> dict[str, Any]

The metadata.json record of the run as it stands (provenance.metadata_record).

close

close() -> None

Write what is queued, close the output files and write the final metadata.json.

run

run(
    steps: int | None = None,
    schedule: Any = None,
    mc: Any = ...,
) -> None

Advance steps steps, or along schedule to its end (§7.2 "Schedules and step counts").

schedule=None continues the attached schedule, or keeps the target without one. mc=... (the default) keeps the attached move set, mc=None detaches it, a move set attaches it in place of any attached one (§7.6): one fm.mc record (Swap, Transmute, Displace) or a sequence of them. Every check runs before the first step: a schedule above Workload.max_temperature_K raises PlanRefused, steps beyond the schedule ValueError, and the move set that will be attached (given or kept) is checked against the workload over every step of this call (PlanRefused; SpeciesNotInSuperset for a species outside the superset).

switch_tier

switch_tier(request)

The tier switch of SPEC §9.6 at the committed step, for request (a planner.adapt.SwitchRequest): the compiled revision, built from the pristine model, validated, then given the committed state. A candidate that fails leaves the run on its revision, with a warning (adapt.SWITCH_FAILED_CODE); returns whether the switch was made. Not inside an MC block, nor from an uncommitted step.

add_metadata_block

add_metadata_block(name: str, provider: Any) -> None

Add {name: provider()} to every metadata.json record this Simulation builds, evaluated at each write: the fastmace.ase classes' "ase" block (4-T10), in the run's own durable write instead of a second one per run(). name must not be a key of the record itself.

relax

relax(
    *, fmax: float = 0.05, steps: int = 100000000
) -> tuple[Any, dict[str, Any]]

Relax with the optimiser (SPEC §10.3; 4-T05): ASE's Optimizer.run(fmax=, steps=) on the engine.

A converged start takes no step; otherwise one step at a time until the largest constraint-adjusted force is below fmax (eV/Angstrom) or steps steps are done, one force call per step. A second call continues with the optimiser's history, as a second run of one ASE optimiser does. Returns the relaxed Atoms (a copy of the structure at the committed positions and cell, no calculator) and the summary of fastmace.optimize.driver.relax: steps, converged, seconds, initial and final energies per atom, fmax, the per-step energies and max forces, and the engine's neighbour rebuilds and re-captures.

save_restart

save_restart(path: Any) -> None

Write the complete §10.6 state at the current committed step, a canonical restart point.

path is <directory>/restart.npz (or the directory): Simulation.resume(directory) reads it there. The output queue is drained and the output chunks end here (§10.6), the engine is canonicalised and written by io.restart.write_engine_restart (T05's format), and simulation.json next to it holds what that format does not: the model source, the integrator record, workload, Output, seed, allow_naive_cueq, the planner options, species, masses and PBC. A plan that runs in a child copies the child's last hand-off, a canonical restart point at the committed step (2-T07).

Only at a committed step: refused (RuntimeError) inside an MC block and while the engine holds an uncommitted (overflowed) step, before anything is written (§10.6, 3-T08).

resume classmethod

resume(
    directory: Any,
    *,
    allow_plan_change: bool = False,
    check: Check = "skip",
) -> Simulation

Continue from the restart in directory. A different force-field identity raises RestartMismatch, except that another MACE or FastMACE version alone warns and is recorded as a plan revision and in the report (owner decision 23); a different plan needs allow_plan_change=True and makes a new revision (§9.5, §10.6). check as in the constructor.

The model is loaded again from simulation.json's file, checked against its SHA-256; the engine is built at the restart's positions, cell and composition (species and masses at the restart step, not the initial structure's; 3-T08) through the planner, then takes the restart's state, edge bucket and neighbour capacity (io.restart.resume_engine). Output chunks after the restart point are deleted and new ones opened; nothing is rewritten.

set_positions

set_positions(positions: Any) -> None

Host write of positions at a canonical point: quiesce, write, rebuild, re-evaluate (owner decision D4). positions in Å, shape (N, 3); the momenta stay.

set_momenta

set_momenta(momenta: Any) -> None

Host write of momenta at a canonical point (owner decision D4). momenta in ASE units, shape (N, 3); the engine integrates velocities, momenta / masses.

update

update(
    *,
    species: Any = None,
    masses: Any = None,
    charges: Any = None,
    total_charge: Any = None,
    friction_per_fs: Any = None,
    positions: Any = None,
    momenta: Any = None,
    cell: Any = None,
) -> None

Any combination of host changes as one transaction with one force evaluation (SPEC §8.5, §7.7).

  • species: atomic numbers or symbols, (N,); in the ForceField's superset (SpeciesNotInSuperset). Masses follow the new species' standard masses unless masses is given too;
  • masses: amu, (N,); the momenta stay (ASE), so the velocities change;
  • charges: per-atom charges, (N,), and total_charge, for models that take them;
  • friction_per_fs: the Langevin friction in 1/fs (the record's unit); it drops the captured graphs, which bake it in (a recorded re-capture), and with the default barostat τ moves P_tau and the barostat mass with it;
  • positions (Å) and momenta (ASE units), (N, 3); momenta use the new masses;
  • cell (Å, (3, 3), rows the lattice vectors), fixed-cell runs only (4-T05; 4-T09 relaxes quench frames with their own NPT cells on one engine): the positions are not scaled, so give them too. The lists and cell inputs change in place, so the captured graphs stay unless the rebuild grows a capacity or the edge count needs a new padded size.

Everything is validated before anything is written. The changes apply in this order: species, masses, charges, friction, cell, positions, momenta; then a canonical point (neighbour list rebuilt at the committed positions, forces and stress evaluated once, integrator.start), as a host write of positions (owner decision D4). Nothing given: nothing happens.

set_species

set_species(species: Any) -> None

Set every atom's species (atomic numbers or symbols, shape (N,)), one event. Each must be in the ForceField's superset (SpeciesNotInSuperset); masses follow the new species' standard masses unless set_masses follows.

set_masses

set_masses(masses: Any) -> None

Set every atom's mass in amu, shape (N,), one event; the momenta stay (ASE), so the velocities change: v' = v m_old / m_new.

set_charges

set_charges(
    charges: Any, *, total_charge: float | None = None
) -> None

Set the per-atom charges (models that take them), shape (N,), and optionally the total charge, one event. charges=None with total_charge changes the total only.

set_friction

set_friction(friction_per_fs: float) -> None

Set the Langevin friction in 1/fs, one event; it drops the graphs that bake c1/c2 in (a recorded re-capture).

Committed dataclass

The committed step as read-only host arrays: what an observer sees (Simulation.committed).

stress (3×3, eV/ų) is None for fixed-cell integrators; energy in eV. The composition (3-T03): species atomic numbers (N,), masses amu (N,), charges (N,) or None for models that take none.