Skip to content

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)

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
evaluate_pairs(pos, neighbors, factor=None)

(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
pair_energy_force(r2, disp)

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
evaluate(pos)

Energy and forces at pos ((N, 3)): (energy, forces), forces a float64 (N, 3) array.

Potentials

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
evaluate(arg)

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

intramolecular_pairs(
    frame, forcefield=None, neighbors=None
)

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
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:"}, optional The style's LAMMPS form (:func: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()

evaluate classmethod
evaluate(q=None, *, x=None, **params)

:func:evaluate of this style.

unregister classmethod
unregister()

:func:unregister_style this style.