Packmol parity¶
molpack tracks the original Packmol's behavior (L. Martínez et al.,
J. Comput. Chem. 2009). Coordinates are not byte-identical with the
same seed, but functional equivalence is enforced on five canonical
workloads.
For script syntax, use the official Packmol user guide. This page is about parity checks, accepted differences, and molpack-specific extensions.
Paper figures¶
Canonical workloads¶

The five Packmol examples are run unchanged through Packmol and molpack. Coordinate identity is not the target; valid packing and matching spatial distributions are.
Thread scaling¶

The fused objective-and-gradient kernel is too small to parallelize profitably for tiny systems, then crosses over and scales on larger systems.
What is matched¶
Objective structure
- Geometric restraint penalties — equivalent of
comprest/gwalls. - Minimum-distance overlap penalty — equivalent of
computef/fparc.
Optimization workflow
- Initialization with constraint-only fitting (
initial/restmol/swaptype). avoid_overlap(on by default, faithful toinitial.f90): initial placements that land inside a fixed molecule are rejected. This matters for dense solvation around a large fixed solute — without it a sizeable fraction of solvent seeds inside the solute, roughly doubling the initial overlap and slowing convergence by about an order of magnitude.- Phased main optimization — per-type pre-compaction, then all-types.
movebadheuristic for stalled molecules.- Radius-scaling (
radscale) decay across each phase. - GENCAN / SPG / CG inner solver chain.
- Precision gate on
fdist(overlap) andfrest(restraint violation).
Restraint vocabulary
inside/outside box,cube,sphere,ellipsoid,cylinderover plane(above) /below plane- fixed molecule placement
All twelve box/cube/sphere/ellipsoid/cylinder/plane kinds lower through a single
restraint_from_spec table in script::build, so each is reachable from both
whole-molecule and atoms … end atoms blocks. The two Gaussian-surface kinds
(14/15) exist in the Rust/Python API but have no .inp keyword yet — Packmol's
Gaussian grammar is not pinned down here, and emitting a wrong parameter mapping
would silently mis-pack, so the parser rejects them rather than guessing.
Periodicity
pbc X Y Zandpbc X0 Y0 Z0 X1 Y1 Z1, mirroring Packmol'sgetinp.f90. The packer's cell grid is built directly from the declared box.
Script parser strictness
- Unknown top-level keywords are rejected via
ScriptError::UnknownKeyword. A silently droppedpbcleaves the cell grid sized by the ±sidemaxinitial placement, a 42 GB allocation on a measured workload; strict parsing guards against that class of failure.
Determinism
- Explicit seeds; identical seed values are used for paired Packmol vs molpack runs.
Verification¶
Batch example validation¶
The five canonical workloads ship as runnable programs under examples/:
Each prints the same quantities a parity check needs — atom and molecule counts against the expanded target specs, and the violation metrics against tolerance / precision. There is no automated batch harness for them; a measurement system for packing quality is being designed.
Violation metrics¶
Each side reports max_distance_violation, max_constraint_penalty,
violating_pairs, and violating_atoms. Both packers must satisfy the
same thresholds.
Accepted differences¶
- Packed coordinates are not bit-identical with Packmol, even at the same seed — the inner solver and RNG are independent implementations.
- The two Gaussian-surface restraint kinds have no
.inpkeyword; they are reachable only from the Rust/Python API. - Acceptance is therefore functional rather than numerical: the same restraints, the same conflict criteria, the same order of magnitude of tolerance / precision, and comparable violation metrics.