Skip to content

molpy.pack

BoxRegion

BoxRegion(lengths, origin=None, coord_field='xyz')

Bases: Region

Source code in src/molpy/core/region.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(
    self,
    lengths: ArrayLike,
    origin: ArrayLike | None = None,
    coord_field: str = "xyz",
):
    super().__init__(coord_field)
    self.lengths = np.asarray(lengths, dtype=float)
    if origin is None:
        origin = np.zeros(3)
    self.origin = np.asarray(origin, dtype=float)

    # Validate dimensions
    if self.lengths.shape != (3,):
        raise ValueError(f"lengths must be 3D, got shape {self.lengths.shape}")
    if self.origin.shape != (3,):
        raise ValueError(f"origin must be 3D, got shape {self.origin.shape}")

mask

mask(block)

Extract coordinates from block and apply geometric predicate.

Source code in src/molpy/core/region.py
92
93
94
95
def mask(self, block: Block) -> np.ndarray:  # type: ignore[override]
    """Extract coordinates from block and apply geometric predicate."""
    coords = block[self.coord_field]
    return self.isin(coords)

Molpack

Molpack(workdir, packer='packmol')

High-level molecular packing interface.

This class provides a clean API for molecular packing with the ability to choose different packer backends.

Usage

packer = Molpack(workdir=Path("packing"), packer="packmol") packer.add_target(frame, number=100, constraint=box_constraint) result = packer.optimize(max_steps=1000, seed=42)

Initialize Molpack.

Parameters:

Name Type Description Default
workdir Path

Working directory for packing operations

required
packer Literal['packmol', 'nlopt']

Packer backend to use ("packmol" or "nlopt")

'packmol'
Source code in src/molpy/pack/molpack.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def __init__(
    self,
    workdir: Path,
    packer: Literal["packmol", "nlopt"] = "packmol",
):
    """
    Initialize Molpack.

    Args:
        workdir: Working directory for packing operations
        packer: Packer backend to use ("packmol" or "nlopt")
    """
    if not workdir.exists():
        workdir.mkdir(parents=True, exist_ok=True)
    self.workdir = workdir
    self.targets = []
    self.packer = get_packer(packer, workdir=workdir)

add_target

add_target(frame, number, constraint)

Add a packing target.

Parameters:

Name Type Description Default
frame Frame

Frame containing the molecule structure

required
number int

Number of copies to pack

required
constraint Any

Spatial constraint for packing

required

Returns:

Type Description
Target

Target object

Source code in src/molpy/pack/molpack.py
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def add_target(self, frame: Frame, number: int, constraint: Any) -> Target:
    """
    Add a packing target.

    Args:
        frame: Frame containing the molecule structure
        number: Number of copies to pack
        constraint: Spatial constraint for packing

    Returns:
        Target object
    """
    target = Target(frame, number, constraint)
    self.targets.append(target)
    self.packer.add_target(target)
    return target

optimize

optimize(max_steps=1000, seed=None)

Run packing optimization.

Parameters:

Name Type Description Default
max_steps int

Maximum optimization steps

1000
seed int | None

Random seed. If None, uses random seed.

None

Returns:

Type Description
Frame

Packed Frame

Source code in src/molpy/pack/molpack.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def optimize(self, max_steps: int = 1000, seed: int | None = None) -> Frame:
    """
    Run packing optimization.

    Args:
        max_steps: Maximum optimization steps
        seed: Random seed. If None, uses random seed.

    Returns:
        Packed Frame
    """
    if seed is None:
        seed = random.randint(1, 10000)
    # Use __call__ method instead of legacy pack() method
    return self.packer(self.targets, max_steps=max_steps, seed=seed)

Packmol

Packmol(executable=None, workdir=None)

Bases: Packer

Packer implementation using Packmol binary.

This class provides a packer that uses Packmol binary for packing molecules.

Usage

packer = Packmol(executable="packmol") result = packer(targets, max_steps=1000, seed=4628)

Initialize Packmol packer.

Parameters:

Name Type Description Default
executable str | None

Path to packmol executable. If None, uses: 1. PACKMOL_BIN environment variable 2. "packmol" on PATH

None
workdir Path | None

Working directory for temporary files. If None, creates a temporary directory.

None
Source code in src/molpy/pack/packer/packmol.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(
    self,
    executable: str | None = None,
    workdir: Path | None = None,
):
    """
    Initialize Packmol packer.

    Args:
        executable: Path to packmol executable. If None, uses:
                   1. PACKMOL_BIN environment variable
                   2. "packmol" on PATH
        workdir: Working directory for temporary files.
                If None, creates a temporary directory.
    """
    Packer.__init__(self)
    self.executable = executable
    self.workdir = workdir

generate_input_only

generate_input_only(targets=None, max_steps=1000, seed=4628, tolerance=2.0, workdir=None)

Generate Packmol input file without running the packing.

Parameters:

Name Type Description Default
targets list[Target] | None

List of packing targets. If None, uses stored targets.

None
max_steps int

Maximum optimization steps

1000
seed int

Random seed for packing

4628
tolerance float

Distance tolerance in Angstroms

2.0
workdir Path | None

Optional working directory

None

Returns:

Type Description
Path

Path to generated input file

Source code in src/molpy/pack/packer/packmol.py
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
def generate_input_only(
    self,
    targets: list[Target] | None = None,
    max_steps: int = 1000,
    seed: int = 4628,
    tolerance: float = 2.0,
    workdir: Path | None = None,
) -> Path:
    """
    Generate Packmol input file without running the packing.

    Args:
        targets: List of packing targets. If None, uses stored targets.
        max_steps: Maximum optimization steps
        seed: Random seed for packing
        tolerance: Distance tolerance in Angstroms
        workdir: Optional working directory

    Returns:
        Path to generated input file
    """
    if targets is None:
        targets = self.targets

    if not targets:
        raise ValueError("No targets provided")

    workdir = workdir if workdir is not None else self.workdir
    if workdir is None:
        workdir = Path(tempfile.mkdtemp(prefix="molpack_"))
    else:
        workdir = Path(workdir)
        workdir.mkdir(parents=True, exist_ok=True)

    input_file = self._generate_input(targets, max_steps, seed, tolerance, workdir)
    return input_file

generate_input_script

generate_input_script(targets=None, max_steps=1000, seed=4628, tolerance=2.0, workdir=None)

Generate Packmol input script as a Script object without saving.

This allows you to preview, edit, or format the script before saving.

Parameters:

Name Type Description Default
targets list[Target] | None

List of packing targets. If None, uses stored targets.

None
max_steps int

Maximum optimization steps

1000
seed int

Random seed for packing

4628
tolerance float

Distance tolerance in Angstroms

2.0
workdir Path | None

Optional working directory (for structure file paths)

None

Returns:

Type Description
Script

Script object containing Packmol input

Raises:

Type Description
ValueError

If no targets provided

Source code in src/molpy/pack/packer/packmol.py
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
def generate_input_script(
    self,
    targets: list[Target] | None = None,
    max_steps: int = 1000,
    seed: int = 4628,
    tolerance: float = 2.0,
    workdir: Path | None = None,
) -> Script:
    """
    Generate Packmol input script as a Script object without saving.

    This allows you to preview, edit, or format the script before saving.

    Args:
        targets: List of packing targets. If None, uses stored targets.
        max_steps: Maximum optimization steps
        seed: Random seed for packing
        tolerance: Distance tolerance in Angstroms
        workdir: Optional working directory (for structure file paths)

    Returns:
        Script object containing Packmol input

    Raises:
        ValueError: If no targets provided
    """
    if targets is None:
        targets = self.targets

    if not targets:
        raise ValueError("No targets provided")

    workdir = workdir if workdir is not None else self.workdir
    if workdir is None:
        workdir = Path(tempfile.mkdtemp(prefix="molpack_"))
    else:
        workdir = Path(workdir)
        workdir.mkdir(parents=True, exist_ok=True)

    return self._generate_input_script(targets, max_steps, seed, tolerance, workdir)

pack

pack(targets=None, max_steps=1000, seed=None)

Pack molecules using Packmol backend.

This method implements the abstract pack() method from Packer base class. It delegates to call() for the actual implementation.

Source code in src/molpy/pack/packer/packmol.py
548
549
550
551
552
553
554
555
556
557
558
559
def pack(
    self,
    targets: list[Target] | None = None,
    max_steps: int = 1000,
    seed: int | None = None,
) -> Frame:
    """Pack molecules using Packmol backend.

    This method implements the abstract pack() method from Packer base class.
    It delegates to __call__() for the actual implementation.
    """
    return self(targets, max_steps=max_steps, seed=seed)

SphereRegion

SphereRegion(radius, center=None, coord_field='xyz')

Bases: Region

Source code in src/molpy/core/region.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
def __init__(
    self, radius: float, center: ArrayLike | None = None, coord_field: str = "xyz"
):
    super().__init__(coord_field)
    self.radius = float(radius)
    if center is None:
        center = np.zeros(3)
    self.center = np.asarray(center, dtype=float)

    # Validate dimensions and values
    if self.radius <= 0:
        raise ValueError(f"radius must be positive, got {self.radius}")
    if self.center.shape != (3,):
        raise ValueError(f"center must be 3D, got shape {self.center.shape}")

mask

mask(block)

Extract coordinates from block and apply geometric predicate.

Source code in src/molpy/core/region.py
163
164
165
166
def mask(self, block: Block) -> np.ndarray:  # type: ignore[override]
    """Extract coordinates from block and apply geometric predicate."""
    coords = block[self.coord_field]
    return self.isin(coords)

get_packer

get_packer(name, *args, **kwargs)

Factory function to get a packer by name.

Parameters:

Name Type Description Default
name str

Packer name ("packmol" or "nlopt")

required
*args Any

Positional arguments passed to packer constructor

()
**kwargs Any

Keyword arguments passed to packer constructor

{}

Returns:

Type Description
Packmol

Packer instance

Raises:

Type Description
NotImplementedError

If packer name is not recognized

Source code in src/molpy/pack/packer/__init__.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def get_packer(name: str, *args: Any, **kwargs: Any) -> Packmol:
    """
    Factory function to get a packer by name.

    Args:
        name: Packer name ("packmol" or "nlopt")
        *args: Positional arguments passed to packer constructor
        **kwargs: Keyword arguments passed to packer constructor

    Returns:
        Packer instance

    Raises:
        NotImplementedError: If packer name is not recognized
    """
    if name == "packmol":
        return Packmol(*args, **kwargs)
    elif name == "nlopt":
        # Nlopt packer not yet implemented
        raise NotImplementedError(
            f"Packer '{name}' not yet implemented. Use 'packmol' instead."
        )
    else:
        raise NotImplementedError(
            f"Packer '{name}' not recognized. Available: 'packmol'"
        )