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}