- For the System object: A way to map between applied parameters in a System and the underlying graph molecule(s)
- For ParameterHandlers: A way to look at a Topology and determine
- how many parameters should be assigned
- whether one SMIRKS parameter should clobber another, or both parameters should coexist
- In formal math: An customizable language (algebra?) for describing how to take a graph where each node has a unique index and:
- enumerate desired graph substructures
- define an equivalence operator for graph substructures
>>> from openforcefield.topology import Molecule
>>> from openforcefield.typing.engines.smirnoff import ForceField
>>> ff = ForceField('openff_unconstrained-1.2.0.offxml')
>>> labels = ff.label_molecules(Molecule.from_smiles('C(=O)O').to_topology())
>>> for key in labels[0]:
... print(key)
... for key2, value2 in labels[0][key].items():
... print(key2, value2)
...
Bonds
(0, 1) <BondType with smirks: [#6:1]=[#8X1+0,#8X2+1:2] id: b20 length: 1.225108345696 A k: 1216.657338681 kcal/(A**2 mol) >
(0, 2) <BondType with smirks: [#6X3:1]-[#8X2H1:2] id: b17 length: 1.369205578614 A k: 775.7511246779 kcal/(A**2 mol) >
(0, 3) <BondType with smirks: [#6X3:1]-[#1:2] id: b84 length: 1.085503378387 A k: 808.4160937 kcal/(A**2 mol) >
(2, 4) <BondType with smirks: [#8:1]-[#1:2] id: b87 length: 0.9738225805594 A k: 1113.990711326 kcal/(A**2 mol) >
Angles
(0, 2, 4) <AngleType with smirks: [*:1]-[#8:2]-[*:3] angle: 110.9462337492 deg k: 127.9119052761 kcal/(mol rad**2) id: a27 >
(1, 0, 2) <AngleType with smirks: [#8X1:1]~[#6X3:2]~[#8:3] angle: 129.0541431805 deg k: 410.9822194847 kcal/(mol rad**2) id: a15 >
(1, 0, 3) <AngleType with smirks: [#1:1]-[#6X3:2]~[*:3] angle: 133.1339832262 deg k: 68.40592742547 kcal/(mol rad**2) id: a11 >
(2, 0, 3) <AngleType with smirks: [#1:1]-[#6X3:2]~[*:3] angle: 133.1339832262 deg k: 68.40592742547 kcal/(mol rad**2) id: a11 >
ProperTorsions
(1, 0, 2, 4) <ProperTorsionType with smirks: [#1:1]-[#8X2:2]-[#6X3:3]=[#8X1:4] periodicity1: 2 periodicity2: 1 phase1: 180.0 deg phase2: 0.0 deg id: t100 k1: 2.237928151469 kcal/mol k2: 1.23728649144 kcal/mol idivf1: 1.0 idivf2: 1.0 >
(3, 0, 2, 4) <ProperTorsionType with smirks: [*:1]~[#6X3:2](=[#8,#16,#7])-[#8:3]-[#1:4] periodicity1: 2 phase1: 180.0 deg id: t99 k1: 2.529110648699 kcal/mol idivf1: 1.0 >
ImproperTorsions
(1, 0, 2, 3) <ImproperTorsionType with smirks: [*:1]~[#6X3:2](~[#8X1:3])~[#8:4] periodicity1: 2 phase1: 180.0 deg k1: 10.5 kcal/mol id: i2 >
vdW
(0,) <vdWType with smirks: [#6:1] epsilon: 0.086 kcal/mol id: n14 rmin_half: 1.908 A >
(1,) <vdWType with smirks: [#8:1] epsilon: 0.21 kcal/mol id: n17 rmin_half: 1.6612 A >
(2,) <vdWType with smirks: [#8X2H1+0:1] epsilon: 0.2104 kcal/mol id: n19 rmin_half: 1.721 A >
(3,) <vdWType with smirks: [#1:1]-[#6X3](~[#7,#8,#9,#16,#17,#35])~[#7,#8,#9,#16,#17,#35] epsilon: 0.015 kcal/mol id: n9 rmin_half: 1.359 A >
(4,) <vdWType with smirks: [#1:1]-[#8] epsilon: 5.27e-05 kcal/mol id: n12 rmin_half: 0.3 A >
The keys in the above dictionary are our current solution to "slots".
We've made our own dictionary subclasses like ValenceDict
and ImproperDict
with custom __keytransform__
methods which operate by canonically ordering the atom indices in each SMARTS match, such that they produce intentional key collisions.
For example, a Bond SMARTS that matches particles (2,1) should collide with a previous match on (1,2).
In this case, the desired algorithm is simple -- the particle indices are simply sorted.
This becomes slightly more complex for Angles, where naive sorting may mangle the identity of the central atom, which would mangle the calculation of the geometric angle. So the central atom in an Angle is "priveleged", and is immune to the sorting.
Ditto for ProperTorsions and the two central positions. However, for ProperTorsions, we encounter the complexity that, IF the outer atoms are switched by the sorting, THEN the inner atoms must also be switched.
For ImproperTorsions, the second/central atom is "priveleged", and the other three atoms are sorted.
However, this sorting simply identifies the ordering for key collisions.
After key collisions resolve which improper parameter should be applied, the "trefoil" rules apply the desired improper three times, each with 1/3rd of the actual k
value, on three "canonical" orderings, which are determined by the Topology's indexing system.
Specifically, of the six possible permutations of three non-central atoms, it selects the three where the first particle index in the tuple is smaller than the last.
The selection of these three sortings is funcitonally important -- It makes the energy of the improper deterministic.
If the three permutations were not assigned this way, the same geometry might yield different energies for different atom orderings.
LibraryCharges introduce the possibility of "decomposability" and "partial overlap". Partially-overlapping LibraryCharge SMARTS might be used when many molecules have a common or repeating substructure, but their charges differ for a small functional group. In effect, a LibraryCharge parameter with N tagged atoms is applied as if it had been decomposed into N separate LibraryCharge parameters with one tagged atom each.