Parametrizing non-periodic topologies with mainline force fields changes non-bonded settings
See original GitHub issueDescribe the bug
This is a long-standing mismatch between the SMIRNOFF spec and the implementation of ForceField.create_openmm_system
. I’m not sure how to fix it here, nor am I sure it should be fixed here. I took a stab at something that might fix it in #920, but it blew up in complexity and won’t be implemented here. It’s also been discussed several times in scattered locations, but I don’t think there is a stub issue for it. This is similar to https://github.com/openforcefield/openff-toolkit/issues/882 in its subtlety, impact to users, and general tangled-up-ness.
To Reproduce
Using the README snippet as a starting point:
>>> import openmm
>>> from openff.toolkit.topology import Molecule
>>> from openff.toolkit.utils import get_data_file_path
>>> sdf_file_path = get_data_file_path('molecules/ethanol.sdf')
>>> molecule = Molecule.from_file(sdf_file_path)
>>>
>>> # Create an OpenFF Topology object from the molecule
>>> from openff.toolkit.topology import Topology
>>> topology = Topology.from_molecules(molecule)
>>>
>>> # Load the latest OpenFF force field definition
>>> from openff.toolkit.typing.engines.smirnoff import ForceField
>>> forcefield = ForceField('openff-1.2.0.offxml')
>>> forcefield['vdW'].method
'cutoff'
>>> forcefield['Electrostatics'].method
'PME'
>>> topology.box_vectors # Returns nothing, since it's None
>>> openmm_system = forcefield.create_openmm_system(topology)
We have now parametrized a non-periodic topology with an electrostatics section that’s supposed to use PME, which is not valid for non-periodic topologies. This is handled by converting the entire non-bonded force (recall that out of the box, OpenMM combines vdW and electrostatics interactions into one object) into something that does not have a cutoff:
>>> for force in openmm_system.getForces():
... if type(force) == openmm.openmm.NonbondedForce:
... print(force.getNonbondedMethod())
... print(force.getCutoffDistance())
...
0
0.9 nm
>>> import openmm
>>> openmm.NonbondedForce.NoCutoff
0
This is about the best that can be done with electrostatics (inherent PME errors aside), but it effectively changes the vdW cutoff method from a hard cutoff at 9 Angstroms to no cutoff at all. The energy difference of this is likely small for gas-phase systems, since the few non-bonded interactions are either 1-4 interactions that are scaled down or intramolecular. I don’t have an intuition for how much this would affect fitting, i.e. torsions.
Since this mismatch is a matter of translating details from our state to OpenMM (or any engine), Interchange sees no problem with it and will happily parametrize it, storing the methods of each handler:
>>> from openff.interchange.components.interchange import Interchange
>>> interchange = Interchange.from_smirnoff(force_field=forcefield, topology=topology)
>>> interchange['vdW'].cutoff
<Quantity(9.0, 'angstrom')>
>>> interchange['vdW'].method
'cutoff'
>>> interchange['Electrostatics'].method
'pme'
But when it comes time to convert out to an OpenMM system, it raises an error because of this mismatch:
>>> interchange.to_openmm(combine_nonbonded_forces=True)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mwt/miniconda3/envs/openff-dev/lib/python3.8/site-packages/openff/interchange/components/interchange.py", line 303, in to_openmm
return to_openmm_(self, combine_nonbonded_forces=combine_nonbonded_forces)
File "/Users/mwt/miniconda3/envs/openff-dev/lib/python3.8/site-packages/openff/interchange/interop/openmm.py", line 55, in to_openmm
_process_nonbonded_forces(
File "/Users/mwt/miniconda3/envs/openff-dev/lib/python3.8/site-packages/openff/interchange/interop/openmm.py", line 290, in _process_nonbonded_forces
raise UnsupportedCutoffMethodError
openff.interchange.exceptions.UnsupportedCutoffMethodError
The error is not descriptive enough in v0.1.0 (v0.1.1 says “Combination of non-bonded cutoff methods 9.0 A (vdW) and pme (Electrostatics) not currently supported with combine_nonbonded_forces=True
and .box=None
”), but this is only hit because I’ve hard-coded against this invalid combination. Other engines should error out for similar reasons, but that’s out of scope here.
I’ve added a flag that will tackle cases like this by splitting out interactions into different force objects. In this case, that means electrostatics and vdW interactions will be treated separately and can have different cutoff methods. This is more consistent with what’s specified in the force field.
>>> openmm_system = interchange.to_openmm(combine_nonbonded_forces=False)
>>> for force in openmm_system.getForces():
... if type(force) == openmm.NonbondedForce: # vdW case, NonbondedForce.CutoffNonPeriodic
... print(force.getNonbondedMethod(), force.getCutoffDistance())
... elif type(force) == openmm.CustomNonbondedForce: # Electrostatics case, NonbondedForce.PME
... print(force.getNonbondedMethod())
...
1 0.9 nm
4
This flag is True
by default now, in order to most closely match what the toolkit does right now. Flipping it handles the problem, I believe, but would result in a significant behavior change for users expecting the same behavior from Interchange.from_smirnoff(...).to_openmm()
. Behavior changes include
- Differences in which combinations of inputs run with and without error
- The structure of the returned object, i.e. how many forces and which data is in which force
- Likely subtle energy differences resulting from implementation details such as PME vs. non-cutoff electrostatics, cutoff vs. non-cutoff vdW interactions, etc.
A hack to force the matter now is to convert the molecule to a topology, set its periodicity to some arbitrary large value, and then continue. However, this results in unnecessary and expensive PME calculations that can slow down single-molecule calculations, which force field fitting workflows expect should be fast. It also introduces unnecessary error, both from PME approximations and potential impacts of the selection of box size.
Again, I am not sure what the solution here should be. There probably is no clean solution. The best approach may involve not fixing it here at all, which would mean that Interchange will either need to forcibly introduce this bad behavior or accept the differences in energy comparisons and other impacts to users.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:6 (6 by maintainers)
Sorry to not be succinct at all, I think upon revisiting this I can summarize the key tension here as a response to your question
This is what I would like to do, but I suspect it will be unpopular with most of our users given how often the same few lines of code
are used. Having this raise an exception would be following the rules but I suspect cause enough grief to users that we wouldn’t want it to be the case (especially given that the proposition is adding an error case that didn’t exist before). Or at least that’s been my assumption - I assume this would break existing single-molecule/gas-phase workflows?
The only other idea I’d throw out is somehow encoding “use PME if periodic, and no cutoff if not” into the SMIRNOFF spec. I’m not saying it’s a great idea, but I think it’s how most workflows operate, how our force fields are likely to be used, and I believe how they are fitted. If this is something people want to iterate on further, I can move that discussion to the standards tracker.
This has now been resolved after OFF-EP-0005 and its implementation. There are now separate electrostatics methods for periodic and non-periodic potential, ensuring we are no longer forced to guess how charges should be treated in the gas phase when only given the instructions “use PME”