nvalchemi.dynamics.hooks.WrapPeriodicHook#
- class nvalchemi.dynamics.hooks.WrapPeriodicHook(frequency=1)[source]#
Wrap atomic positions back into the simulation cell under PBC.
During long molecular dynamics trajectories, atomic positions drift away from the unit cell as the integrator applies unbounded displacements. While physically valid (forces are invariant under lattice translations), large coordinates can cause problems:
Neighbor list overflow — distance calculations may exceed the numerical range of the cell-shift representation, leading to missed interactions or incorrect forces.
Precision loss — large coordinate magnitudes reduce the effective floating-point precision available for inter-atomic distances.
Visualization artifacts — trajectories with unwrapped coordinates are difficult to analyze and visualize.
This hook wraps positions back into the unit cell by computing fractional coordinates, taking their modulo, and converting back to Cartesian:
frac = positions @ inv(cell) frac = frac % 1.0 positions = frac @ cell
The wrapping is applied in-place to
batch.positionsand respects per-system periodicity flags inbatch.pbc:If
batch.pbcis[True, True, True], all three dimensions are wrapped.If
batch.pbcis[True, True, False](e.g. a slab), only the x and y coordinates are wrapped; the z coordinate is left unwrapped to allow vacuum gaps.If
batch.pbcis[False, False, False](non-periodic), the hook is a no-op for that system.
The hook fires at
AFTER_POST_UPDATE, after velocities have been updated but before the next step begins. This ensures that the neighbor list built at the start of the next step sees wrapped coordinates.- Parameters:
frequency (int, optional) – Wrap positions every
frequencysteps. Default1(every step). For simulations with moderate drift, wrapping every 10–100 steps is sufficient and reduces overhead.
- frequency#
Wrapping frequency in steps.
- Type:
int
- stage#
Fixed to
AFTER_POST_UPDATE.- Type:
Examples
>>> from nvalchemi.dynamics.hooks import WrapPeriodicHook >>> hook = WrapPeriodicHook(frequency=10) >>> dynamics = DemoDynamics(model=model, n_steps=10_000, dt=0.5, hooks=[hook]) >>> dynamics.run(batch)
Notes
Wrapping does not modify velocities, momenta, or forces — only positions. This is correct because forces depend on relative distances (invariant under translation) and velocities are already in Cartesian space.
For triclinic (non-orthorhombic) cells, the fractional-coordinate approach naturally handles skewed lattice vectors.
This hook assumes
batch.cellhas shape(B, 3, 3)with lattice vectors as rows (consistent with ASE convention).In batched simulations, wrapping is applied per-graph using
batch.batchto associate each atom with its cell.
Methods
__init__([frequency])Attributes