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.core.Frame (molpy.Frame is the same class), resolved zero-copy via its FFI capsule. Element symbols come from the "element" atom column, which every molrs reader (molrs.io.read_pdb, molrs.io.read_xyz, …) writes; a frame built in memory must carry it too.

  • 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.core.Frame:

import molrs
import numpy as np

frame = molrs.core.Frame({
    "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.special_bonds  # list[float] — intramolecular skip table
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

import molrs

target = target.with_restraint(
    molrs.core.Cuboid([0, 0, 0], [40, 40, 40])   # a molrs region: origin, lengths
)

Stack multiple restraints by calling .with_restraint() again:

target = (
    target
    .with_restraint(molrs.core.Cuboid([0, 0, 0], [40, 40, 40]))
    .with_restraint(~molrs.core.Sphere([20, 20, 20], 5.0))
)

A subset of atoms

target = target.with_atom_restraint(
    [30, 31],                                     # 0-based Rust-native indices
    molrs.core.HalfSpace([0.0, 0.0, 1.0], [0.0, 0.0, 2.0]),   # z <= 2
)

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.

Packing radii

The packer separates two atoms by the sum of their packing radii; without an override every atom uses the global tolerance / 2. Van der Waals radii from the source file are not used as packing radii.

target = target.with_radius(2.0)                    # every atom
target = target.with_atom_radius(h_indices, 0.85)   # then explicit hydrogen

All-atom chains with explicit hydrogen keep the default depth-3 skip table and shrink hydrogen here (~0.85 Å). Indices are 0-based.

Hydrogens

Lattice growth places these atoms off their backbone neighbour instead of on a lattice site. The default is every atom whose element symbol is H. Pass indices to override that, or [] for a coarse-grained model. An out-of-range index raises ValueError.

target = target.with_hydrogens(h_indices)

molpy.builder.PackingTemplate reads those indices off an assembled Atomistic (element H on mol.to_frame()). template.frame is a molpy.Frame, the same class as molrs.core.Frame:

import molpy as mp
from molpack import Target

template = mp.builder.PackingTemplate(chain)
target = Target(template.frame, 10).with_hydrogens(template.hydrogens)

Intramolecular skip table

Atom pairs close along the chain are exempt from the hard core. The table is per-target data, not an engine knob:

target.special_bonds  # [0.0, 0.0, 0.0, 1.0] by default (depth 3)
cg = target.with_special_bonds([0.0, 0.0, 1.0])  # CG depth 2

Empty, non-finite, or out-of-range weights raise ValueError. Fractional weights are stored and refused later at CbmcGrow.run. See Chain growth for the all-atom vs CG recipe.

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.