Skip to main content

molpack/
lib.rs

1//! # molpack
2//!
3//! Packmol-grade molecular packing in pure Rust. Produces a non-overlapping
4//! arrangement of N molecule types with copy counts and geometric restraints,
5//! using a faithful port of Packmol's GENCAN-driven three-phase algorithm
6//! (Martínez et al. 2009). Correctness is checked against Packmol's reference
7//! output for five canonical workloads.
8//!
9//! This crate was split out of the molrs workspace in 2026 and is now
10//! maintained independently. It depends on the unified `molcrafts-molrs` crate
11//! for shared data structures (always-on `core`) and, behind feature flags, its
12//! file I/O (`io`) and force-field (`ff`) modules.
13//!
14//! ## Documentation map
15//!
16//! This crate is documented in four dedicated modules; start with
17//! [`getting_started`] if you are new.
18//!
19//! - [`getting_started`] — install, hello-world packing, the three
20//!   restraint scopes, handlers, relaxers, PBC, running the canonical
21//!   examples.
22//! - [`concepts`] — every abstraction defined in one place: `AtomRestraint`,
23//!   `Region`, `Relaxer`, `Handler`, `Objective`, `Target`, `Molpack`,
24//!   `PackContext`; the scope equivalence law; the two-scale contract;
25//!   the direction-3 extension pattern.
26//! - [`architecture`] — module map, dependency graph, core-type
27//!   relationships, full `pack()` lifecycle diagram, hot-path
28//!   `evaluate()` walkthrough, invariants, design decisions.
29//! - [`extending`] — tutorials for writing your own `AtomRestraint` /
30//!   `Region` / `Handler` / `Relaxer`; testing + benchmarking
31//!   discipline; common pitfalls; contributing flow.
32//!
33//! Reference material (not rustdoc):
34//!
35//! - [Packmol parity](https://github.com/MolCrafts/molpack/blob/master/docs/packmol_parity.md)
36//!   — kind-number ↔ Rust struct mapping with Fortran pointers.
37//!
38//! ## Quick example
39//!
40//! ```rust,no_run
41//! use molpack::{InsideBoxRestraint, Molpack, Target};
42//!
43//! let positions = [[0.0, 0.0, 0.0], [0.96, 0.0, 0.0], [-0.24, 0.93, 0.0]];
44//! let radii     = [1.52, 1.20, 1.20];
45//!
46//! let target = Target::from_coords(&positions, &radii, 100)
47//!     .with_name("water")
48//!     .with_restraint(InsideBoxRestraint::new([0.0; 3], [40.0, 40.0, 40.0], [false; 3]));
49//!
50//! let frame = Molpack::new()
51//!     .with_tolerance(2.0)
52//!     .with_precision(0.01)
53//!     .with_seed(42)
54//!     .pack(&[target], 200)?;
55//!
56//! let natoms = frame.get("atoms").and_then(|b| b.nrows()).unwrap_or(0);
57//! println!("packed {natoms} atoms");
58//! # Ok::<(), molpack::PackError>(())
59//! ```
60//!
61//! ## Public surface at a glance
62//!
63//! | Category | Items |
64//! |---|---|
65//! | Builder | [`Molpack`], [`MolpackLogLevel`], [`PackResult`] |
66//! | Target  | [`Target`], [`CenteringMode`] |
67//! | AtomRestraint trait + 14 concrete structs | [`AtomRestraint`] + `InsideBox` / `InsideCube` / `InsideSphere` / `InsideEllipsoid` / `InsideCylinder` / `Outside*` variants / `AbovePlane` / `BelowPlane` / `AboveGaussian` / `BelowGaussian` — each suffixed `…AtomRestraint` |
68//! | Region trait + combinators + lift | [`Region`], [`RegionExt`], [`And`], [`Or`], [`Not`], [`RegionRestraint`], [`InsideBoxRegion`], [`InsideSphereRegion`], [`OutsideSphereRegion`], [`Aabb`] |
69//! | Handler trait + built-ins | [`Handler`], [`NullHandler`], [`LammpsLogHandler`], [`ProgressHandler`], [`EarlyStopHandler`], [`XYZHandler`], [`StepInfo`], [`PhaseInfo`], [`PhaseReport`] |
70//! | Relaxer trait + built-ins | [`Relaxer`], [`RelaxerRunner`], [`TorsionMcRelaxer`], `LBFGSRelaxer` (`ff` feature) |
71//! | Errors | [`PackError`] |
72//! | Validation | [`validate_from_targets`], [`ValidationReport`], [`ViolationMetrics`] |
73//! | Examples harness | [`ExampleCase`], [`build_targets`], [`example_dir_from_manifest`], [`render_inp_script`] |
74//!
75//! ## Feature flags
76//!
77//! - `rayon` — opt into the parallel evaluator (also forwards to `molrs`'s
78//!   `rayon`).
79//! - `io` — pull in molrs's `io` module so [`script::Script::build`] can read
80//!   PDB / SDF / XYZ / LAMMPS files directly. PyO3 / WASM / embedding hosts that
81//!   bring their own loader leave this off and use [`script::Script::lower`]
82//!   with [`script::StructurePlan::apply`] instead.
83//! - `cli` — build the `molpack` binary and its integration tests (pulls in
84//!   `clap` and implies `io`).
85//! - `ff` — pull in molrs's `ff` module (MMFF typifier + L-BFGS) and enable the
86//!   `LBFGSRelaxer`, which relaxes a flexible molecule's internal geometry
87//!   during packing under a caller-supplied force field.
88//!
89//! Precision is fixed at `f64` via `molrs::types::F`.
90
91pub mod assemble;
92#[cfg(feature = "io")]
93pub mod cases;
94pub mod cell;
95pub mod constraints;
96pub mod context;
97pub mod error;
98pub mod euler;
99pub mod frame;
100pub mod gencan;
101pub mod handler;
102pub mod initial;
103pub mod movebad;
104mod numerics;
105pub mod objective;
106pub mod packer;
107mod random;
108pub mod region;
109pub mod relaxer;
110pub mod restraint;
111pub mod script;
112pub mod target;
113pub mod validation;
114
115#[cfg(feature = "io")]
116pub use cases::{ExampleCase, build_targets, example_dir_from_manifest, render_inp_script};
117pub use context::PackContext;
118pub use error::PackError;
119pub use frame::{compute_mol_ids, context_to_frame, finalize_frame, frame_to_coords};
120pub use handler::{
121    EarlyStopHandler, Handler, LammpsLogHandler, MolpackLogLevel, NullHandler, PhaseInfo,
122    PhaseReport, ProgressHandler, StepInfo, XYZHandler,
123};
124pub use molrs::Element;
125pub use molrs::types::F;
126pub use packer::{Molpack, PackResult};
127pub use region::{
128    Aabb, And, InsideBoxRegion, InsideSphereRegion, Not, Or, OutsideSphereRegion, Region,
129    RegionExt, RegionRestraint,
130};
131pub use relaxer::{Relaxer, RelaxerRunner, TorsionMcRelaxer};
132// Force-field geometry relaxer + the molrs `Potential` trait it relaxes against
133// (named in `LBFGSRelaxer::new`). Gated on the `ff` feature.
134#[cfg(feature = "ff")]
135pub use molrs::ff::potential::Potential;
136#[cfg(feature = "ff")]
137pub use relaxer::LBFGSRelaxer;
138pub use restraint::{
139    AboveGaussianRestraint, AbovePlaneRestraint, AtomRestraint, BelowGaussianRestraint,
140    BelowPlaneRestraint, InsideBoxRestraint, InsideCubeRestraint, InsideCylinderRestraint,
141    InsideEllipsoidRestraint, InsideSphereRestraint, OutsideBoxRestraint, OutsideCubeRestraint,
142    OutsideCylinderRestraint, OutsideEllipsoidRestraint, OutsideSphereRestraint,
143};
144pub use target::{Angle, Axis, CenteringMode, Placement, Target};
145pub use validation::{ValidationReport, ViolationMetrics, validate_from_targets};
146
147// Custom-objective extension surface. `Molpack::pack` drives a `dyn Objective`
148// through GENCAN; downstream code that implements a bespoke objective (or wants
149// to evaluate the packing energy/gradient directly) names these at the crate
150// root rather than reaching into the `objective` / `constraints` modules.
151pub use constraints::{Constraints, EvalMode, EvalOutput};
152pub use objective::Objective;
153
154// ────────────────────────────────────────────────────────────────────────────
155// Documentation modules (rustdoc-only; no runtime items).
156// Content lives in `docs/*.md`, loaded via `include_str!` so each markdown
157// file can be edited independently while rustdoc renders the whole chapter.
158// ────────────────────────────────────────────────────────────────────────────
159
160#[doc = include_str!("../docs/getting_started.md")]
161pub mod getting_started {}
162
163#[doc = include_str!("../docs/concepts.md")]
164pub mod concepts {}
165
166#[doc = include_str!("../docs/architecture.md")]
167pub mod architecture {}
168
169#[doc = include_str!("../docs/extending.md")]
170pub mod extending {}
171
172// ────────────────────────────────────────────────────────────────────────────
173// Prelude — bulk re-export of the vocabulary a typical packing script needs.
174// ────────────────────────────────────────────────────────────────────────────
175
176/// Bulk re-export of the items a typical packing script needs.
177///
178/// ```no_run
179/// use molpack::prelude::*;
180///
181/// let target = Target::from_coords(&[[0.0, 0.0, 0.0]], &[1.0], 10)
182///     .with_restraint(InsideBoxRestraint::new([0.0; 3], [10.0; 3], [false; 3]));
183/// let frame = Molpack::new().pack(&[target], 100)?;
184/// # Ok::<(), molpack::PackError>(())
185/// ```
186///
187/// The crate root still re-exports everything for direct `use molpack::T`
188/// access; the prelude exists to avoid a 20-line `use` block at the top of
189/// every example.
190pub mod prelude {
191    pub use crate::{
192        // Region + combinators + lift
193        Aabb,
194        // AtomRestraint trait + 14 concrete impls
195        AboveGaussianRestraint,
196        AbovePlaneRestraint,
197        And,
198        // Target + centering + angle / axis / placement
199        Angle,
200        AtomRestraint,
201        Axis,
202        BelowGaussianRestraint,
203        BelowPlaneRestraint,
204        CenteringMode,
205        // Handlers
206        EarlyStopHandler,
207        Handler,
208        InsideBoxRegion,
209        InsideBoxRestraint,
210        InsideCubeRestraint,
211        InsideCylinderRestraint,
212        InsideEllipsoidRestraint,
213        InsideSphereRegion,
214        InsideSphereRestraint,
215        // Core builder + result + error
216        LammpsLogHandler,
217        Molpack,
218        MolpackLogLevel,
219        Not,
220        NullHandler,
221        Or,
222        OutsideBoxRestraint,
223        OutsideCubeRestraint,
224        OutsideCylinderRestraint,
225        OutsideEllipsoidRestraint,
226        OutsideSphereRegion,
227        OutsideSphereRestraint,
228        PackError,
229        PackResult,
230        PhaseInfo,
231        PhaseReport,
232        Placement,
233        ProgressHandler,
234        Region,
235        RegionExt,
236        RegionRestraint,
237        // Relaxer
238        Relaxer,
239        RelaxerRunner,
240        StepInfo,
241        Target,
242        TorsionMcRelaxer,
243        XYZHandler,
244    };
245    // Force-field relaxer (gated; cannot live in the `use` group above because a
246    // single item in a brace group cannot carry its own `cfg`).
247    #[cfg(feature = "ff")]
248    pub use crate::LBFGSRelaxer;
249}