Typifier¶
Graph typification and force-field parameter assignment.
The contract¶
A typifier is Atomistic -> Atomistic: it takes a molecular graph and returns a
typed copy whose atoms and links carry force-field types and parameters. Every
typifier runs the same flow — copy, assign, write the annotations back and define
the types — so typify() is written once, on the base class, and assign() is
the single step a typifier implements.
class Typifier:
def typify(self, mol) -> Atomistic # final: copy, assign, write back
def assign(self, graph) -> TypeAssignment # the only thing that differs
def source_forcefield(self) -> ForceField # optional: what the output declares
def forcefield(self) -> ForceField # the union of every type typify assigned
typify is final — a subclass that defines it raises TypeError at class
creation — and it is the only writer of forcefield().
Typifiers are named after the force field or the tool that decides the types.
Quick reference¶
| Symbol | Summary | Preferred for |
|---|---|---|
Typifier |
The contract: implement assign (and optionally source_forcefield) |
Writing your own |
TypeAssignment |
What assign returns: node and link annotations, styles, pair rows |
Writing your own |
OplsAaTypifier |
Full OPLS-AA typing pipeline (native) | OPLS-AA all-atom force fields |
Mmff94Typifier / Mmff94sTypifier |
Full MMFF94 / MMFF94s typing pipeline (native) | MMFF all-atom force fields |
ElementTypifier |
type labels from element symbols; defines no force field |
Writers that need labels on an untyped molecule |
AtdTypifier |
AtdTypifier(parameter_set="gaff2"): antechamber's atom-type tables (gaff, gaff2, amber, bcc, …) evaluated natively — atom types only, no charges or parameters |
GAFF / GAFF2 or BCC atom types without running antechamber |
GaffTypifier |
GaffTypifier(parameter_set="gaff2"): GAFF / GAFF2 bonded terms and parameters, natively, for atoms AtdTypifier already typed (exact gaff.dat rows, wildcards, or parmchk2-style estimates marked estimated) |
GAFF / GAFF2 parameters without AmberTools |
AntechamberTypifier |
AntechamberTypifier(parameter_set="gaff2"): antechamber → parmchk2 → tleap for one complete molecule; needs AmberTools |
An antechamber charge method other than AM1-BCC / ABCG2; exporting an estimated GAFF term while molrs's LAMMPS / GROMACS writers refuse it |
TleapTypifier |
TleapTypifier(parameter_set="gaff2"): tleap alone over a finished graph that already carries AMBER types and charges (a graph with ports is refused); needs AmberTools |
Extra frcmod parameters on top of the leaprc |
Every name is on mp.ff.typifier, which mirrors molrs.ff.typifier (the
native typifiers are the molrs objects) and adds the two AmberTools
typifiers. GAFF / GAFF2 is typed natively by AtdTypifier then
GaffTypifier; that is the default route, and it assigns the types and
parameter values tleap does for the same set. Its AM1-BCC charges come from
mp.ff.charge.Am1BccModel: sqm's AM1 charges (through antechamber -c mul),
corrected by the native BccModel exactly as antechamber -c bcc corrects
them. A native GAFF force field merges with one read from tleap's prmtop, an
AmberPolymerBuilder chain's included, so a small molecule mixed with a
chain is typed natively too. The AmberTools typifiers read tleap's prmtop
back; they are for what only that pipeline does: parameters of your own on
top of the leaprc (TleapTypifier) and antechamber's other charge methods
(AntechamberTypifier). molrs 0.18.0's LAMMPS and GROMACS writers refuse the
estimated parameter GaffTypifier puts on a term it estimated, so such a
molecule is exported through AntechamberTypifier for now. Every GAFF entry
point takes the same required parameter_set ("gaff" or "gaff2", no
default). The AmberTools typifiers shell out through molpy.wrapper; see
Optional external tools.
A GAFF polymer is not typed by joining typed monomers (each join folds the
leaving group's charge onto its anchor): build it with
mp.builder.AmberPolymerBuilder, which cuts one antechamber-typed
oligomer with prepgen and joins the residues with tleap — see
Builder and the
AmberTools guide.
UFF typing is in the native core (Rust) only; it has no Python binding.
Canonical example¶
import molpy as mp
from molpy.ff.typifier import OplsAaTypifier
mol, _ = mp.conformer.Conformer(add_hydrogens=True, seed=42).generate(
mp.io.smiles.read_smiles("CCO").to_atomistic()
)
typifier = OplsAaTypifier(strict=True)
typed_mol = typifier.typify(mol) # returns a new Atomistic
ff = typifier.forcefield() # the parameters of the types just assigned
frame = typed_mol.to_frame()
Key behavior¶
typify()returns a new graph — the original is not modifiedforcefield()is the union of what everytypifycall assigned, as an independent copy; a definition that contradicts one already held raises- A term the force field does not parameterise is left undecided, never
stamped with
None - SMARTS matching is native; MolPy carries no matcher of its own
Writing a typifier¶
Implement assign and stop. It returns a TypeAssignment: one annotation mapping per
node, positional against graph.atoms, and per link class one mapping per row,
positional against graph.links.exact_bucket(cls). A type annotation is
(style, name, endpoints, params): it stamps name and every param, and
defines the type name on endpoints (atom-type names, empty for an atom
type). The name is never parsed — the endpoints are required. styles declares
each style used; pairs adds (style, name, endpoints, params) pair rows.
class Tip3pTypifier(mp.ff.typifier.Typifier):
"""TIP3P water: OW / HW atoms and one OW-HW bond type."""
SITES = {"O": ("OW", 15.999, -0.834), "H": ("HW", 1.008, 0.417)}
def source_forcefield(self):
return mp.ff.forcefield.ForceField("tip3p", units="real") # the output declares real units
def assign(self, graph):
nodes = []
for atom in graph.atoms:
name, mass, charge = self.SITES[atom["element"]]
nodes.append({"type": ("full", name, (), {"mass": mass}), "charge": charge})
bonds = [
{"type": ("harmonic", "OW-HW", ["OW", "HW"], {"k": 450.0, "r0": 0.9572})}
for _ in graph.links.exact_bucket(mp.Bond)
]
return mp.ff.typifier.TypeAssignment(
nodes,
{mp.Bond: bonds},
styles=[("atom", "full", {}), ("bond", "harmonic", {}), ("pair", "lj/cut", {})],
pairs=[
("lj/cut", "OW", ["OW"], {"epsilon": 0.1521, "sigma": 3.1507}),
("lj/cut", "HW", ["HW"], {"epsilon": 0.0, "sigma": 0.0}),
],
)
tip3p = Tip3pTypifier()
water = tip3p.typify(mp.io.smiles.read_smiles("[H]O[H]").to_atomistic())
assert [bond["type"] for bond in water.bonds] == ["OW-HW", "OW-HW"]
assert tip3p.forcefield().get_style("bond", "harmonic").get_type_by_name("OW-HW")["k"] == 450.0
Related¶
- Guide: Force Field Typification
- Guide: AmberTools Integration
- Concepts: Force Field
- Extending Typifiers
Full API¶
Typifiers¶
typifier ¶
Force-field typification — :mod:molrs.ff.typifier, plus the AmberTools typifiers.
The native typifiers are identity re-exports
(mp.ff.typifier.OplsAaTypifier is molrs.ff.typifier.OplsAaTypifier).
Typifier is the base every typifier extends: a subclass implements
assign (and optionally source_forcefield) and the base owns typify
and the accumulated forcefield().
:func:assign_cmaps builds a typed frame's cmaps block.
GAFF / GAFF2 is typed natively: AtdTypifier (antechamber's atom types)
then GaffTypifier (the bonded terms), each taking molrs's required
parameter_set; AM1-BCC charges come from
:class:molpy.ff.charge.Am1BccModel (sqm, then the native correction).
That force field merges with an :class:~molpy.builder.AmberPolymerBuilder
chain's. molpy adds :class:AntechamberTypifier and :class:TleapTypifier,
which run the whole AmberTools pipeline (:mod:molpy.wrapper) and read
tleap's prmtop back, for antechamber's other charge methods and for
parameters of your own on top of the leaprc. They take the same required
parameter_set.
AntechamberTypifier ¶
Bases: AmberTypifier
GAFF / GAFF2 atom types, charges and parameters for a complete molecule.
assign runs antechamber (-at parameter_set -c charge_method, net charge
from the atoms' formal charges), parmchk2 and tleap in a directory under
work_dir keyed by the input mol2 and every setting, and assigns the
prmtop's types, charges and bonded terms. Every program reruns, and each
step's output is deleted before it runs, so nothing an earlier run left
behind is read. Ports are allowed; their leaving groups are real atoms.
A polymer chain is not typed here: joining typed monomers folds each
leaving group's charge onto its anchor. Build it with
:class:molpy.builder.AmberPolymerBuilder, which cuts one
antechamber-typed oligomer with prepgen and sequences it with tleap.
The default GAFF route is native: :class:~molrs.ff.typifier.AtdTypifier
and :class:~molrs.ff.typifier.GaffTypifier assign the same types and
parameter values, and :class:Am1BccModel the same AM1-BCC charges, with
sqm the only program; that force field merges with an
:class:~molpy.builder.AmberPolymerBuilder chain's as this one does.
This typifier is for an antechamber charge method other than AM1-BCC /
ABCG2. Its prmtop force field also carries no estimate provenance, which
molrs 0.18.0's LAMMPS and GROMACS writers refuse on a term
GaffTypifier estimated.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parameter_set
|
ParameterSet
|
The GAFF generation, molrs's |
required |
charge_method
|
str
|
The antechamber |
'bcc'
|
work_dir
|
str | Path
|
Where the per-run directories are created. |
'amber_work'
|
config
|
Config | None
|
molpy's configuration (:func: |
None
|
AtdTypifier ¶
antechamber atom types — one rule engine over seven ATOMTYPE_*.DEF tables.
parameter_set is the antechamber -at flag and is required: seven
tables exist, they disagree, and there is no default. An atom no rule matches
comes back labelled "DU" — the table's own catch-all row, not a fallback.
bond_orders says which bond orders the types follow:
BondOrderSource.Connectivity (the core default) judges them from the
connectivity alone, as antechamber's bondtype -j full does, ignoring
the molecule's own; BondOrderSource.Stated keeps the molecule's
orders.
ElementTypifier ¶
molrs.ff.typifier.ElementTypifier — type labels from atomic
numbers alone, with no force field.
Atoms get type = the symbol their atomic_number names (e.g.
"C"); bonds, angles and dihedrals get their endpoint symbols joined
with - in the byte-wise smaller orientation (bond O–H is "H-O").
:meth:forcefield stays empty. Exported from :mod:molrs.ff.typifier
only.
Raises¶
ValueError
From :meth:typify, when an atom has no valid atomic_number or
is the wildcard (0), or the molecule has impropers.
GaffTypifier ¶
GAFF / GAFF2 bonded terms and parameters for a molecule whose atoms
already carry GAFF types (AtdTypifier with the same parameter_set).
Angles and dihedrals are regenerated from the bond graph, impropers built
as tleap builds them (wherever tleap finds a row, or parmchk2 an
estimate at a centre PARMCHK.DAT flags as planar; tleap's atom order,
centre third); each term is an exact gaff.dat / gaff2.dat row, a wildcard row, or a
parmchk2-style estimate whose type carries estimated,
estimate_penalty, estimate_method and estimate_analog. The
four price nothing: every engine writer (LAMMPS, GROMACS, OpenMM, frcmod)
passes them over, and ForceField.merge ignores them.
parameter_set is required; there is no default.
Raises¶
ValueError
An unknown parameter_set; from :meth:typify, an untyped atom, a
type the table does not declare, or terms nothing covers (all listed).
Mmff94Typifier ¶
MMFF94 (Halgren 1996) atom types, charges and bonded parameters.
The variant is the class, never a flag. See Mmff94sTypifier for the
"static" parameter set.
typify labels the graph; PotentialCompiler(forcefield()).compile(frame)
compiles it. There is no one-step build — MMFF walks the same route as every other
force field.
Mmff94sTypifier ¶
MMFF94s (Halgren 1999) — the "static" set, for energy minimization.
Differs from Mmff94Typifier only on delocalised trivalent nitrogen (MMFF
numeric types 10 NC=O / 40 NC=C): 11 out-of-plane rows and 42 torsion
rows are re-parameterised so the nitrogen minimizes planar. typify bakes
koop = +0.015 (type 10) / +0.030 (type 40) mdArad^-2 on those
centres, where the improper kernel reads it as
E_oop = 0.5 * 143.9325 * koop * chi**2 (chi in radians). Every atom type
and every bond / angle / stretch-bend / vdW / charge parameter is shared with
MMFF94.
OplsAaTypifier ¶
TleapTypifier ¶
Bases: AmberTypifier
Parameterise an already typed graph with tleap alone.
The graph's atoms must carry AMBER atom types and charges. assign
writes them as mol2, writes forcefield (the templates' parameters) as a
frcmod, runs tleap and assigns the prmtop's bonded terms, so junction terms
come from the leaprc. The run directory is keyed by the mol2, the
parameter set and the frcmod, and tleap's outputs are deleted before it runs. It never
runs antechamber or parmchk2. Types and charges are left as they are;
tleap changing one raises. A graph that
still has ports is refused: joining those ports would fold the
leaving-group charge onto the anchors. A polymer chain is built with
:class:molpy.builder.AmberPolymerBuilder, whose prepgen step
spreads that charge instead.
:class:~molrs.ff.typifier.GaffTypifier assigns the same bonded terms
natively, and its force field merges with an
:class:~molpy.builder.AmberPolymerBuilder chain's; this typifier is for
parameters of your own on top of the leaprc (forcefield).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parameter_set
|
ParameterSet
|
The GAFF generation, molrs's |
required |
forcefield
|
ForceField | None
|
Parameters tleap loads on top of the leaprc, written as a
frcmod; |
None
|
work_dir
|
str | Path
|
Where the per-run directories are created. |
'amber_work'
|
config
|
Config | None
|
molpy's configuration (:func: |
None
|
TypeAssignment ¶
What a typifier's assign assigns to one graph.
nodes is positional against graph.atoms; links maps a relation
kind to rows positional against that kind's own rows, so an improper never
shifts a dihedral position. A key is a relation view class (Bond,
Angle, Dihedral, Improper, Port; rows as graph.bonds …
graph.ports) or a block name ("bonds", or a term block such as
"urey_bradleys"; rows as graph.terms(block)). A type annotation under a kind defines a
type of the category whose block the kind is (urey_bradleys →
urey_bradley). Any other key raises TypeError, a kind named twice
ValueError. styles are (category, style, params) to declare,
in order; pairs are (style, name, endpoints, params) pair rows.
Typifier ¶
Bases: Generic[_TGraph]
The base of every graph typifier: one assign hook plus the output
force field its typing accumulates.
A subclass implements assign (and optionally source_forcefield) and nothing
else; defining typify on a subclass raises TypeError at class
creation. The native classes extend this base and only construct; they
are subclassable, but a subclass of one that defines assign or
source_forcefield raises TypeError (they run in Rust).
assign ¶
Type graph and return what it assigns.
assign may write intermediate results (generated topology, perceived
bond types) onto the graph it is given; typify always gives it a
private copy. The base raises NotImplementedError; a native class
runs its Rust matcher.
forcefield ¶
The accumulated output — exactly the definitions typify has
assigned — returned as a copy.
Edits to the copy do not reach the typifier; typify is the only
writer. Before the first typify it is the seeded empty output.
source_forcefield ¶
The force field this typifier matches against, returned as a copy.
The output starts as its empty likeness (name, declared units and
special_bonds). A Python subclass that does not override it raises
NotImplementedError, and its output starts as an empty force field
named after the class.
typify ¶
Do not override; the only writer of forcefield().
Copies mol (as copy.copy(mol)), calls assign on the copy,
and writes the match onto the copy and the output. Returns the typed copy; mol is untouched.
mol must be an Atomistic (anything else raises TypeError).
Raises NotImplementedError without an assign and ValueError
when the match does not fit the graph or contradicts the output (which
is then unchanged).