Skip to content

Targets

A Target describes one type of molecule to pack: its template geometry, element symbols, and the number of copies to produce. VdW radii are resolved automatically from element symbols via the Bondi (1964) table.

Construction

from molpack import Target

target = Target(frame, count)
  • frame — a molrs.Frame or molpy.Frame, resolved zero-copy via its FFI capsule. Supported sources:
Source Element column
molrs.read_pdb(path) "symbol"
molrs.read_xyz(path) "element"
molrs.Frame.from_dict({"blocks": {"atoms": {...}}}) "element"
molpy.Frame "element"
  • count — number of copies to produce.

A display label is optional:

target = Target(frame, count).with_name("water")

Build a frame in memory (no PDB file) with molrs.Frame.from_dict:

import molrs
import numpy as np

frame = molrs.Frame.from_dict({
    "blocks": {
        "atoms": {
            "x": np.array([0.00,  0.96, -0.24]),
            "y": np.array([0.00,  0.00,  0.93]),
            "z": np.zeros(3),
            "element": ["O", "H", "H"],
        }
    }
})
water = Target(frame, count=100).with_name("water")

Read-only properties

target.name        # Optional[str]
target.natoms      # number of template atoms
target.count       # requested copies
target.elements    # list[str]
target.radii       # list[float]
target.is_fixed    # True if placement is frozen (see below)

All builder methods are immutable — they return a new Target.

Centering

The default is CenteringMode.AUTO — free targets are centered on their geometric center before packing; fixed targets are kept in place. Override explicitly:

from molpack import CenteringMode

target = target.with_centering(CenteringMode.CENTER)  # always center
target = target.with_centering(CenteringMode.OFF)     # keep input coords

Fixed placement

Pin a target at a specific location (e.g. a fixed reference molecule):

from molpack import Angle

target = target.fixed_at([10.0, 20.0, 30.0])

# optional Euler orientation — three Angle values in Packmol's
# eulerfixed convention
target = (
    target.fixed_at([10.0, 20.0, 30.0])
    .with_orientation((
        Angle.from_degrees(0.0),
        Angle.from_radians(1.57),
        Angle.ZERO,
    ))
)

Fixed targets are excluded from the optimizer but still contribute to distance exclusion against other species.

Rotation bounds

Restrict the rotational search window about each axis:

from molpack import Angle, Axis

target = (
    target
    .with_rotation_bound(Axis.X, Angle.from_degrees(0.0),  Angle.from_degrees(15.0))
    .with_rotation_bound(Axis.Y, Angle.from_degrees(90.0), Angle.from_degrees(10.0))
    .with_rotation_bound(Axis.Z, Angle.from_degrees(0.0),  Angle.from_degrees(5.0))
)

Angle makes units explicit: use Angle.from_degrees(...) or Angle.from_radians(...) — raw floats are rejected.

Attaching restraints

All atoms of the target

from molpack import InsideBoxRestraint

target = target.with_restraint(
    InsideBoxRestraint([0, 0, 0], [40, 40, 40])
)

Stack multiple restraints by calling .with_restraint() again:

target = (
    target
    .with_restraint(InsideBoxRestraint([0, 0, 0], [40, 40, 40]))
    .with_restraint(OutsideSphereRestraint([20, 20, 20], 5.0))
)

A subset of atoms

from molpack import AbovePlaneRestraint, BelowPlaneRestraint

target = target.with_atom_restraint(
    [30, 31],                                     # 0-based Rust-native indices
    BelowPlaneRestraint([0.0, 0.0, 1.0], 2.0),
)

0-based indexing

with_atom_restraint uses 0-based indices, matching Rust convention. If you are porting from a Packmol .inp file (which uses 1-based indices), subtract 1 at the call site.

Relaxation-assisted packing

Attach an in-loop relaxer to reshape a flexible molecule's reference geometry during packing — useful for long chains that must fold to fit. with_relaxer requires count == 1 (every copy shares the reference geometry the relaxer rewrites):

from molpack import Target, TorsionMcRelaxer

chain = TorsionMcRelaxer(frame).with_steps(20).with_self_avoidance(1.5)
target = Target(frame, count=1).with_relaxer(chain)

Two relaxers ship built in: TorsionMcRelaxer (engine-free Monte-Carlo torsion sampling, always available) and LBFGSRelaxer (force-field L-BFGS, ff feature). See In-loop relaxers for their options.

Per-target solver budget

Override the maximum perturbation budget for this target:

target = target.with_perturb_budget(50)  # default: derived from count

Useful when one species is significantly harder to place than the rest.