Skip to main content
The packing API provides a Python interface to pack molecules into simulation boxes using Packmol-inspired algorithms. It supports complex constraints, multiple structure types, and various output formats.

Quick Start

from warp_md.pack import PackConfig, Structure, Box, run, export

# Define structures to pack
structures = [
    Structure(path="protein.pdb", count=1, fixed=True),
    Structure(path="water.pdb", count=5000)
]

# Create configuration
config = PackConfig(
    structures=structures,
    box=Box(size=(50.0, 50.0, 50.0)),
    min_distance=2.0,
    seed=12345
)

# Run packing
result = run(config)

# Export to PDB
export(result, fmt="pdb", path="packed.pdb")

Core Classes

PackConfig

Main configuration class for packing simulations.
structures
List[Structure]
required
List of structures to pack into the box
box
Box
required
Simulation box specification
seed
int
default:"0"
Random seed for reproducibility
max_attempts
int
default:"10000"
Maximum packing attempts per molecule
min_distance
float
default:"2.0"
Minimum distance between molecules (Angstroms)
pbc
bool
default:"False"
Enable periodic boundary conditions
filetype
str
Override file format detection (e.g., “pdb”, “xyz”)
add_box_sides
bool
default:"False"
Add box dimensions to output file
add_amber_ter
bool
default:"False"
Add AMBER-style TER records
output
OutputSpec
Output file specification (alternative to using export())

Advanced Parameters

maxit
int
Maximum iterations for optimization
nloop
int
Number of optimization loops
avoid_overlap
bool
default:"True"
Check for overlaps during packing
precision
float
Coordinate precision for optimization
restart_from
str
Checkpoint file to restart from
restart_to
str
Checkpoint file to save to

Methods

def validate() -> None:
    """Validate configuration parameters."""

def to_dict() -> Dict[str, Any]:
    """Convert to dictionary representation."""

@classmethod
def from_dict(cls, data: Dict[str, Any]) -> PackConfig:
    """Create from dictionary."""

Structure

Defines a molecular structure to pack.
path
str
required
Path to structure file (PDB, XYZ, etc.)
count
int
default:"1"
Number of copies to pack
name
str
Optional structure name/label
fixed
bool
default:"False"
Keep structure fixed in space (no rotation/translation)
rotate
bool
default:"True"
Allow random rotations
center
bool
default:"True"
Center structure before packing
positions
List[Tuple[float, float, float]]
Fixed positions for each copy
translate
Tuple[float, float, float]
Translation vector to apply
chain
str
Chain ID to assign
constraints
List[Constraint]
Spatial constraints for packing
radius
float
Override atomic radii
min_distance
float
Override minimum distance for this structure

Example: Constrained Packing

from warp_md.pack import Structure, Constraint

# Pack 100 waters inside a sphere
water = Structure(
    path="water.pdb",
    count=100,
    constraints=[
        Constraint(
            mode="inside",
            shape="sphere",
            center=(25.0, 25.0, 25.0),
            radius=15.0
        )
    ]
)

# Pack protein outside the sphere
protein = Structure(
    path="protein.pdb",
    count=1,
    fixed=True,
    constraints=[
        Constraint(
            mode="outside",
            shape="sphere",
            center=(25.0, 25.0, 25.0),
            radius=15.0
        )
    ]
)

Box

Simulation box specification.
size
Tuple[float, float, float]
required
Box dimensions (x, y, z) in Angstroms
shape
str
default:"orthorhombic"
Box shape: “orthorhombic”, “cubic”, or “triclinic”
# Cubic box
box = Box(size=(50.0, 50.0, 50.0), shape="cubic")

# Rectangular box
box = Box(size=(60.0, 40.0, 40.0), shape="orthorhombic")

Constraint

Spatial constraints for structure placement.
mode
str
required
Constraint mode: “inside”, “outside”, “fixed”, “over”, “below”
shape
str
required
Constraint shape: “box”, “sphere”, “cylinder”, “cube”, “ellipsoid”, “gaussian”, “plane”

Shape Parameters

Box:
min
Tuple[float, float, float]
required
Minimum corner coordinates
max
Tuple[float, float, float]
required
Maximum corner coordinates
Sphere:
center
Tuple[float, float, float]
required
Center coordinates
radius
float
required
Sphere radius
Cylinder:
base
Tuple[float, float, float]
required
Base point coordinates
axis
Tuple[float, float, float]
required
Cylinder axis vector
radius
float
required
Cylinder radius
height
float
required
Cylinder height
Plane:
point
Tuple[float, float, float]
required
Point on the plane
normal
Tuple[float, float, float]
required
Plane normal vector

PackResult

Packing result containing packed system data.
coords
numpy.ndarray
Atomic coordinates (N x 3 array)
box
Tuple[float, float, float]
Final box dimensions
name
List[str]
Atom names
element
List[str]
Element symbols
resname
List[str]
Residue names
resid
List[int]
Residue IDs
chain
List[str]
Chain identifiers
charge
List[float]
Atomic charges
mol_id
List[int]
Molecule IDs
bonds
List[Tuple[int, int]]
Bond connectivity list
segid
List[str]
Segment identifiers

Functions

run

Execute packing with given configuration.
def run(cfg: Union[PackConfig, Dict[str, Any]]) -> PackResult:
    """Run packing simulation.
    
    Args:
        cfg: PackConfig object or dictionary representation
        
    Returns:
        PackResult containing packed system
        
    Raises:
        RuntimeError: If native bindings unavailable
        ValidationError: If configuration invalid
    """
Example:
from warp_md.pack import PackConfig, Structure, Box, run

config = PackConfig(
    structures=[Structure(path="mol.pdb", count=10)],
    box=Box(size=(30.0, 30.0, 30.0)),
    seed=42
)

result = run(config)
print(f"Packed {len(result.coords)} atoms")

export

Export packing result to file.
def export(
    result: PackResult,
    fmt: str,
    path: str,
    scale: Optional[float] = None,
    *,
    add_box_sides: bool = False,
    box_sides_fix: float = 0.0,
    write_conect: bool = True,
    hexadecimal_indices: bool = False
) -> None:
    """Export packed system to file.
    
    Args:
        result: PackResult to export
        fmt: Output format (pdb, xyz, gro, cif, mol2, crd)
        path: Output file path
        scale: Coordinate scaling factor
        add_box_sides: Include box dimensions in output
        box_sides_fix: Box dimension adjustment
        write_conect: Write connectivity records
        hexadecimal_indices: Use hex indices for PDB
    """
Supported formats:
  • pdb - Protein Data Bank format
  • xyz - XYZ coordinates
  • gro - GROMACS format
  • cif / mmcif - Crystallographic Information File
  • mol2 - Tripos MOL2 format
  • crd - CHARMM/AMBER coordinate file
  • lammps / lammps-data - LAMMPS data file
Example:
from warp_md.pack import run, export

result = run(config)

# Export to PDB with box info
export(result, fmt="pdb", path="system.pdb", add_box_sides=True)

# Export to GROMACS format
export(result, fmt="gro", path="system.gro")

# Export with coordinate scaling
export(result, fmt="xyz", path="system.xyz", scale=0.1)  # nm instead of Angstrom

run_inp

Run packing from Packmol .inp file.
def run_inp(path: str) -> PackResult:
    """Run packing from Packmol-format input file.
    
    Args:
        path: Path to .inp file
        
    Returns:
        PackResult containing packed system
    """
Example:
from warp_md.pack import run_inp, export

# Run from existing Packmol input
result = run_inp("system.inp")
export(result, fmt="pdb", path="output.pdb")

parse_inp

Parse Packmol .inp file to Python dict.
def parse_inp(path: str) -> Dict[str, Any]:
    """Parse Packmol input file to configuration dict.
    
    Args:
        path: Path to .inp file
        
    Returns:
        Dictionary representation of configuration
    """
Example:
from warp_md.pack import parse_inp, PackConfig, run

# Parse and modify configuration
config_dict = parse_inp("template.inp")
config_dict["seed"] = 999
config_dict["min_distance"] = 2.5

# Create config and run
config = PackConfig.from_dict(config_dict)
result = run(config)

Complete Example

from warp_md.pack import (
    PackConfig, Structure, Box, Constraint,
    run, export, water_pdb
)

# Get bundled water model
water_path = water_pdb("tip3p")

# Define membrane in center
membrane = Structure(
    path="membrane.pdb",
    count=1,
    fixed=True,
    center=True
)

# Pack water above and below membrane
water_above = Structure(
    path=water_path,
    count=2000,
    constraints=[
        Constraint(
            mode="over",
            shape="plane",
            point=(0.0, 0.0, 5.0),
            normal=(0.0, 0.0, 1.0)
        )
    ]
)

water_below = Structure(
    path=water_path,
    count=2000,
    constraints=[
        Constraint(
            mode="below",
            shape="plane",
            point=(0.0, 0.0, -5.0),
            normal=(0.0, 0.0, 1.0)
        )
    ]
)

# Configure and run
config = PackConfig(
    structures=[membrane, water_above, water_below],
    box=Box(size=(80.0, 80.0, 100.0)),
    min_distance=2.0,
    seed=12345,
    pbc=True
)

result = run(config)

# Export with box info for MD
export(
    result,
    fmt="pdb",
    path="membrane_system.pdb",
    add_box_sides=True,
    write_conect=True
)

print(f"Packed system: {len(result.coords)} atoms")
print(f"Box: {result.box}")

Water Models

Bundled water models are available:
from warp_md.pack import water_pdb, available_water_models

# List available models
print(available_water_models())  # ['tip3p', 'tip4p', 'spce', ...]

# Get path to water model
tip3p = water_pdb("tip3p")
spce = water_pdb("spce")

Error Handling

from warp_md.pack import PackConfig, ValidationError

try:
    config = PackConfig(
        structures=[],  # Empty - invalid!
        box=Box(size=(10.0, 10.0, 10.0))
    )
    config.validate()
except ValidationError as e:
    print(f"Invalid configuration: {e}")

See Also

Build docs developers (and LLMs) love