molpack/restraint/mod.rs
1//! `AtomRestraint` trait and concrete soft-penalty types for molecular packing.
2//!
3//! Each `*Restraint` struct is a **concrete, independent type** holding its own
4//! geometric parameters — no `Builtin*` wrapper, no tagged-union `{kind, params[9]}`
5//! blob, no builder pattern. User extensions `impl AtomRestraint` identically and sit
6//! beside the 14 Packmol-originals in type space.
7//!
8//! Numerical equivalence to the Fortran `comprest.f90` (value) and `gwalls.f90`
9//! (gradient) is preserved branch-for-branch; see `docs/packmol_parity.md`.
10//!
11//! **Gradient convention**: `AtomRestraint::fg` accumulates INTO `g` with `+=`.
12//! Do not overwrite; many restraints may contribute to the same atom.
13//!
14//! **Two-scale contract** (Packmol convention): linear penalties
15//! (box / cube / plane, kinds 2/3/6/7/10/11) use `scale`; quadratic penalties
16//! (sphere / ellipsoid / cylinder / gaussian, kinds 4/5/8/9/12/13/14/15) use
17//! `scale2`. Each `impl AtomRestraint` decides internally which to consume.
18//!
19//! Direction-3 rule (see spec §0 bullet 9): all molrs-pack extension points
20//! follow `pub trait X` + N concrete pub structs that `impl X`; user-defined
21//! structs `impl X` the same way. No `Builtin*` prefix, no wrapper, no builder.
22
23use molrs::types::F;
24
25// ============================================================================
26// Trait
27// ============================================================================
28
29/// Soft-penalty restraint evaluated per atom during packing.
30///
31/// - `f` — value only (line-search interpolation)
32/// - `fg` — fused value + gradient; gradient accumulates INTO `g` with `+=`
33/// - `is_parallel_safe` — if `false`, scheduler serializes this restraint
34/// (Python-backed restraints MUST return `false`)
35/// - `name` — human-readable identifier (default: `std::any::type_name::<Self>()`)
36/// - `periodic_box` — opt-in: a restraint may declare that it defines a
37/// periodic axis-aligned box (`min`, `max`, `periodic[k]` per axis).
38/// At most one periodic box may be declared across all restraints on a
39/// packing run; the packer resolves multiple declarations by requiring
40/// identical bounds. Default `None` (non-periodic); only
41/// [`InsideBoxRestraint`] overrides it.
42///
43/// `Debug` is required on concrete impls so `Target` / `Molpack` remain
44/// printable for diagnostics. All built-in restraints derive it; user types
45/// should do the same.
46pub trait AtomRestraint: Send + Sync + std::fmt::Debug {
47 fn f(&self, x: &[F; 3], scale: F, scale2: F) -> F;
48 fn fg(&self, x: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F;
49 fn is_parallel_safe(&self) -> bool {
50 true
51 }
52 fn name(&self) -> &'static str {
53 std::any::type_name::<Self>()
54 }
55 /// Declare that this restraint defines a periodic box for the
56 /// pair-kernel minimum-image wrap. Return `Some((min, max, periodic))`
57 /// where `periodic[k] == true` marks axis `k` as wrapping. Default
58 /// `None` means this restraint does not imply PBC.
59 fn periodic_box(&self) -> Option<([F; 3], [F; 3], [bool; 3])> {
60 None
61 }
62}
63
64/// Blanket impl so `Box<dyn AtomRestraint>` itself implements the trait.
65impl AtomRestraint for Box<dyn AtomRestraint> {
66 #[inline]
67 fn f(&self, x: &[F; 3], scale: F, scale2: F) -> F {
68 (**self).f(x, scale, scale2)
69 }
70 #[inline]
71 fn fg(&self, x: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
72 (**self).fg(x, scale, scale2, g)
73 }
74 #[inline]
75 fn is_parallel_safe(&self) -> bool {
76 (**self).is_parallel_safe()
77 }
78 #[inline]
79 fn name(&self) -> &'static str {
80 (**self).name()
81 }
82 #[inline]
83 fn periodic_box(&self) -> Option<([F; 3], [F; 3], [bool; 3])> {
84 (**self).periodic_box()
85 }
86}
87
88mod collective;
89mod geometric;
90
91pub use collective::{
92 ExponentialPlane, ExponentialPoint, GaussianPlane, GaussianPoint, Restraint, TabulatedPlane,
93 TabulatedPoint,
94};
95pub use geometric::{
96 AboveGaussianRestraint, AbovePlaneRestraint, BelowGaussianRestraint, BelowPlaneRestraint,
97 InsideBoxRestraint, InsideCubeRestraint, InsideCylinderRestraint, InsideEllipsoidRestraint,
98 InsideSphereRestraint, OutsideBoxRestraint, OutsideCubeRestraint, OutsideCylinderRestraint,
99 OutsideEllipsoidRestraint, OutsideSphereRestraint,
100};