Skip to content

Periodic boundaries

By default the packer works under free boundary conditions — atoms are not wrapped and the only geometric limits come from the restraints you attach. Use periodic boundaries (PBC) when packing for MD input.

Enabling PBC

PBC is declared on the InsideBoxRestraint via the periodic keyword:

from molpack import InsideBoxRestraint

# Fully periodic orthorhombic cell
box = InsideBoxRestraint(
    [0.0, 0.0, 0.0],
    [30.0, 30.0, 30.0],
    periodic=(True, True, True),
)

periodic is a 3-tuple of booleans — one per axis. Only orthorhombic cells are supported.

Per-axis PBC is possible — e.g. slab geometry with in-plane PBC and open Z:

slab = InsideBoxRestraint(
    [0.0, 0.0, 0.0],
    [30.0, 30.0, 100.0],
    periodic=(True, True, False),
)

Declaring PBC on the packer

For a fully-periodic cell you can skip the restraint and declare the box directly on the packer (Packmol's pbc keyword):

from molpack import Molpack

packer = Molpack().with_periodic_box([0.0, 0.0, 0.0], [30.0, 30.0, 30.0])

This is equivalent to a single InsideBoxRestraint(..., periodic=(True, True, True)) broadcast over the system. Use the restraint form when you need per-axis control or a soft confining region; use with_periodic_box when the cell is simply fully periodic.

Semantics

Under PBC, the pairwise distance evaluator applies minimum-image wrapping on the periodic axes, so atoms near opposite faces of the cell "see" each other through the periodic images. The tolerance setting still applies and is checked against the wrapped distance.

Restraints themselves (InsideBoxRestraint, InsideSphereRestraint, …) are evaluated in the unwrapped frame — they describe the geometric region as defined, regardless of the periodic cell.

System-wide PBC derivation

At pack() time the packer scans every restraint on every target for a declared periodic box. The rules are:

  • Zero declarations — non-periodic run.
  • One declaration — its bounds define the system PBC.
  • Multiple declarations — they must all agree (same bounds, same per-axis flags). Any mismatch raises ConflictingPeriodicBoxesError.

Errors

A zero-length axis on a periodic box, or max < min on any axis, raises InvalidPBCBoxError at pack() time:

from molpack import InvalidPBCBoxError

try:
    packer.pack(...)
except InvalidPBCBoxError as e:
    ...

Both typed errors inherit from molpack.PackError (which itself is a RuntimeError subclass) — a blanket except PackError catches any packing failure.

Choosing a box

A common pattern: pack into a single periodic InsideBoxRestraint matching the desired cell. The restraint confines atoms softly and simultaneously declares the PBC:

cell_min = [0.0, 0.0, 0.0]
cell_max = [30.0, 30.0, 30.0]

box = InsideBoxRestraint(cell_min, cell_max, periodic=(True, True, True))
target = target.with_restraint(box)

result = Molpack().with_seed(42).pack([target], max_loops=200)

Or broadcast it globally via Molpack.with_global_restraint(box) when several species share the same cell.