Potential¶
Numerical potential energy functions for bonds, angles, dihedrals, and pairs, and the force-field IR they are declared in.
Quick reference¶
The numerical kernels live in the native core. A force field names them
through its styles (ff.def_style(kind, name)); PotentialCompiler binds them
to a typed Frame as a Potentials evaluator. There is no Python-side
potential class per style. Each is molrs's submodule by identity:
mp.ff.potential is molrs.ff.potential (Potentials, WeightedTerms,
PairLjCut, Potential, …), mp.ff.compile is molrs.ff.compile
(PotentialCompiler, ExplicitTerms), mp.ff.ir is the force-field
IR's vocabulary molrs.ff.ir (ParamSpec, StyleSpec, CategorySpec,
IrError, …), and mp.ff.style_registry is molrs.ff.style_registry
(StyleDeclaration, register_style, register_category, styles, …): a new
style or category is registered from Python with nothing rebuilt — see
Extending the Force Field.
| Symbol | Summary | Preferred for |
|---|---|---|
mp.ff.forcefield.ForceField |
Styles and types by category; def_style(kind, name) |
Declaring parameters |
mp.ff.forcefield.BondStyle / mp.ff.forcefield.AngleStyle / mp.ff.forcefield.DihedralStyle / mp.ff.forcefield.ImproperStyle / mp.ff.forcefield.PairStyle |
One kernel name per style, def_type(...) for its parameters |
Bonded and nonbonded terms |
mp.ff.compile.PotentialCompiler |
PotentialCompiler(ff).compile(frame) → Potentials |
Binding a force field to a typed frame |
mp.ff.potential.Potentials |
calc_energy(frame) / calc_forces(frame); push moves more members in |
Energy / force computation |
mp.ff.compile.ExplicitTerms |
ExplicitTerms(category, style, atoms, **params).compile() → Potentials: any registered style over explicit instances |
Assembling terms by hand |
mp.ff.potential.PairLjCut |
The one-type lj/cut kernel an MD integrator feeds from a neighbour list |
mp.md integrators |
mp.ff.potential.Potential |
The protocol: calc_energy_forces(pos) -> (energy, forces) |
Custom (NN / external) forces in MD |
mp.ff.style_registry.StyleDeclaration, register_style, register_category; mp.ff.ir.ParamSpec |
Declare a style (expression or Python kernel) or a category; StyleSpec / CategorySpec are the registered records styles() / categories() return |
Extending the force field |
Canonical example¶
Define styles and types on a ForceField, then compile it against a typed
Frame with mp.ff.compile.PotentialCompiler(ff).compile(frame). There is no
per-style to_potential(), no ff.to_potentials(), and no parameter-array
lookup; the math runs in the high-performance backend.
import molpy as mp
ff = mp.ff.forcefield.ForceField(name="demo", units="real")
atom_style = ff.def_style("atom", "full")
ct = atom_style.def_type("CT", mass=12.011, charge=-0.18, element="C")
hc = atom_style.def_type("HC", mass=1.008, charge=0.06, element="H")
# a type is given its name, then its endpoints; param name is "k", not "k0"
ff.def_style("bond", "harmonic").def_type("CT-HC", ct, hc, k=340.0, r0=1.09)
# A typed frame: an atoms block + a bonds block carrying a "type" column.
frame = mp.Frame(
blocks={
"atoms": {"x": [0.0, 1.2], "y": [0.0, 0.0], "z": [0.0, 0.0], "type": ["CT", "HC"]},
"bonds": {"atomi": [0], "atomj": [1], "type": ["CT-HC"]},
}
)
pots = mp.ff.compile.PotentialCompiler(ff).compile(frame)
energy = pots.calc_energy(frame)
forces = pots.calc_forces(frame)
Related¶
Full API¶
potential ¶
Evaluable force terms — :mod:molrs.ff.potential, by identity.
:class:Potentials evaluates kernels together (calc_energy_forces);
:class:WeightedTerms is kernels each with its special-bonds weights, for a
neighbour-driven integrator; :class:PairLjCut is the one-type lj/cut
kernel the MD integrators feed; :class:Potential is the protocol every
Python force provider satisfies (calc_energy_forces(pos) -> (energy,
forces)). A force field compiles into them through :mod:molpy.ff.compile.
mp.ff.potential.X is molrs.ff.potential.X for every name here.
PairLjCut ¶
LAMMPS pair_style lj/cut: the one-type cut Lennard-Jones / Mie
kernel (n/m exponents) a neighbour loop feeds (MD's nonbond
kernel). A pair list with a row per pair is
ExplicitTerms("pair", "lj/cut", pairs, epsilon=..., sigma=...).compile().
evaluate_pairs ¶
(energy, forces) over every pair of neighbors (built with
disp=True; PairDriven::evaluate_pairs), forces (N, 3) for
the rows of pos; factor is one weight per pair (None: all
ones).
pair_energy_force ¶
Energy and force of one pair (PairPotential::pair_energy_force);
None beyond the cutoff.
Potential ¶
Bases: Protocol
Structured force provider: one method, coordinates in, energy+forces out.
evaluate ¶
Energy and forces at pos ((N, 3)): (energy, forces),
forces a float64 (N, 3) array.
Potentials ¶
Composite of the one Potential concept — itself a potential.
Potentials() is empty; push moves members in (a PairLjCut,
another Potentials such as one ExplicitTerms.compile built, or an
object with evaluate(pos) -> (energy, forces)). The engine is
unit-agnostic: nothing scales the energy or forces implicitly.
evaluate ¶
Energy and forces (Potential::evaluate): (energy, forces),
forces (N, 3). arg is a :class:Frame (a deferred collection
binds its topology first) or flat [x0, y0, z0, ...] coordinates.
WeightedTerms ¶
Kernels for a neighbour-driven evaluation, each with its special-bonds weights.
Opaque: hand it to an integrator. Taking it apart would mean
re-deciding which member is which and how its close neighbours are
scaled -- the two things PotentialCompiler.compile_typed decides once.
intramolecular_pairs ¶
The special-bonds pair list (atomi / atomj / is_14) of a
typed frame; candidates from neighbors when given, else every
i < j pair.
compile ¶
A force field bound to its kernels — :mod:molrs.ff.compile, by identity.
:class:PotentialCompiler compiles a force field against a typed frame into
mp.ff.potential.Potentials (compile), or into
mp.ff.potential.WeightedTerms for a neighbour-driven integrator
(compile_typed); :class:ExplicitTerms builds the kernel of any
style the force-field IR prices over explicit instances (atom indices, one
parameter row per term, as stored: angle values in degrees)::
pots = mp.ff.potential.Potentials()
pots.push(mp.ff.compile.ExplicitTerms("bond", "harmonic", [[0, 1]], k=300.0, r0=1.4).compile())
energy, forces = pots.calc_energy_forces(pos)
mp.ff.compile.X is molrs.ff.compile.X for every name here.
ExplicitTerms ¶
One style's terms over explicit instances
(molrs::ff::compile::ExplicitTerms): atoms (n, arity), each
per-term parameter a number (broadcast) or one value per term, as stored
(angle values in degrees); style parameters (cutoff, coulomb, an
unregistered style's expression) a number or a string. Works for
every registered style, built-in or custom. A parameter the style does
not declare raises TypeError.
compile ¶
The terms' kernel, built as PotentialCompiler.compile builds
it, one type per term. Refusals raise their molrs.ff.ir.IrError
subclass.
PotentialCompiler ¶
Compiles a ForceField into evaluable kernels.
Owns a copy of the force field taken at construction; later edits to that
ForceField do not reach it. compile(frame) binds a typed frame now;
defer() returns Potentials that bind the frame they are evaluated
on; compile_typed(frame) builds the kernels of a neighbour-driven (MD)
evaluation. Both doors price a pair only inside its style's cutoff
(r < cutoff, as LAMMPS; ∞ when the style states none).
ir ¶
The force-field IR as vocabulary — :mod:molrs.ff.ir, by identity.
What a category (:class:CategorySpec), a style (:class:StyleSpec) and its
parameters (:class:ParamSpec) are, and every refusal: an :class:IrError
(a ValueError), one *Error subclass per refusal (ArityError,
DimensionError, …). Registering a style or a category is
:mod:molpy.ff.style_registry's. molpy keeps no parallel IR:
mp.ff.ir.StyleSpec is molrs.ff.ir.StyleSpec.
style_registry ¶
The force-field style registry — :mod:molrs.ff.style_registry, by identity.
Register a new style (:func:register_style, or a :class:StyleDeclaration
subclass) or a new category (:func:register_category) from Python, with
nothing rebuilt; :func:styles / :func:categories describe what is
registered as mp.ff.ir.StyleSpec / mp.ff.ir.CategorySpec records;
:func:evaluate prices one registered style at sample points;
:func:unregister_style takes a style back. Refusals are mp.ff.ir.IrError
subclasses. mp.ff.style_registry.X is molrs.ff.style_registry.X for every
name here.
StyleDeclaration ¶
Declare a force-field IR style as a class; defining the subclass
registers it (:func:register_style).
Attributes¶
category : str
The category (built-in, or from :func:register_category).
name : str
The style name.
params : list of ParamSpec, or dict of {name: dim}
The per-type parameters, in order.
style_params : list of ParamSpec, or dict of {name: dim}, optional
Style-level parameters (cutoff, mixing, special keep their
reserved meanings).
expression : str, optional
The energy as an expression.
special : {"lj", "coul"}, optional
A pair style's special-bonds class.
samples : list of dict, optional
Registration samples, {"q": (lo, hi), <param>: value}.
compound : bool
kernel takes positions x ((n, arity, 3)), not q.
replace : bool
Replace a style of the name registered at run time.
lammps : {"positional", "positional:register_style's lammps).
A kernel(self, q, **params) -> (e, de_dq) method (or
kernel(self, x, **params) -> (e, grad) for a compound style) prices
the style by Python; with an expression beside it the two must agree.
Pass register=False in the class statement for an intermediate base
that declares no style.
Examples¶
import numpy as np from molrs.ff import style_registry class Quartic(style_registry.StyleDeclaration): ... category = "bond" ... name = "quartic/doc" ... params = {"k": "E/L^4", "r0": "L"} ... expression = "k*(r-r0)^4" ... def kernel(self, r, k, r0): ... d = r - r0 ... return k * d4, 4 * k * d3 e, de = Quartic.evaluate([1.5], k=2.0, r0=1.0) float(e[0]), float(de[0]) (0.125, 1.0) Quartic.unregister()