Unverified Commit a4dec7b9 authored by peastman's avatar peastman Committed by GitHub
Browse files

Merge pull request #2273 from tristanic/customgbforces

Customgbforces
parents e1056ad1 d2ea8772
...@@ -39,6 +39,13 @@ from simtk.openmm import CustomGBForce, Discrete2DFunction ...@@ -39,6 +39,13 @@ from simtk.openmm import CustomGBForce, Discrete2DFunction
import simtk.unit as u import simtk.unit as u
from math import floor from math import floor
_have_numpy = False
try:
import numpy
_have_numpy = True
except ImportError:
pass
def strip_unit(value, unit): def strip_unit(value, unit):
"""Strip off any units and return value in unit""" """Strip off any units and return value in unit"""
...@@ -48,7 +55,7 @@ def strip_unit(value, unit): ...@@ -48,7 +55,7 @@ def strip_unit(value, unit):
# GBn and GBn2 lookup tables # GBn and GBn2 lookup tables
d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444, d0= [2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
2.73212,2.78965,2.84705,2.9043,2.96141,3.0184,3.07524,3.13196, 2.73212,2.78965,2.84705,2.9043,2.96141,3.0184,3.07524,3.13196,
3.18854,3.24498,3.30132,3.35752,3.4136, 3.18854,3.24498,3.30132,3.35752,3.4136,
2.31191,2.37017,2.4283,2.48632,2.5442,2.60197,2.65961,2.71711, 2.31191,2.37017,2.4283,2.48632,2.5442,2.60197,2.65961,2.71711,
...@@ -112,7 +119,7 @@ d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444, ...@@ -112,7 +119,7 @@ d0=[2.26685,2.32548,2.38397,2.44235,2.50057,2.55867,2.61663,2.67444,
3.6629,3.71694,3.77095,3.82489,3.8788,3.93265,3.98646,4.04022, 3.6629,3.71694,3.77095,3.82489,3.8788,3.93265,3.98646,4.04022,
4.09395,4.14762,4.20126,4.25485,4.3084] 4.09395,4.14762,4.20126,4.25485,4.3084]
m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529, m0= [0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529,
0.0197547,0.0179109,0.0162844,0.0148442,0.0135647,0.0124243, 0.0197547,0.0179109,0.0162844,0.0148442,0.0135647,0.0124243,
0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255, 0.0114047,0.0104906,0.00966876,0.008928,0.0082587,0.00765255,
0.00710237,0.00660196,0.00614589, 0.00710237,0.00660196,0.00614589,
...@@ -197,8 +204,8 @@ m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529, ...@@ -197,8 +204,8 @@ m0=[0.0381511,0.0338587,0.0301776,0.027003,0.0242506,0.0218529,
0.0199351,0.018442,0.0170909,0.0158654,0.0147514,0.0137365, 0.0199351,0.018442,0.0170909,0.0158654,0.0147514,0.0137365,
0.0128101,0.0119627,0.0111863] 0.0128101,0.0119627,0.0111863]
# Rescale to nanometers # Rescale to nanometers
d0 = [d / 10. for d in d0] d0 = [d/10.0 for d in d0]
m0 = [m * 10. for m in m0] m0 = [m*10.0 for m in m0]
def _get_bonded_atom_list(topology): def _get_bonded_atom_list(topology):
...@@ -232,38 +239,51 @@ def _is_carboxylateO(atom, all_bonds): ...@@ -232,38 +239,51 @@ def _is_carboxylateO(atom, all_bonds):
def _bondi_radii(topology): def _bondi_radii(topology):
""" Sets the bondi radii """ """ Sets the bondi radii """
radii = [0.0 for atom in topology.atoms()] default_radius = 1.5
element_to_radius = {
E.carbon: 1.7,
E.hydrogen: 1.2,
E.deuterium: 1.2,
E.nitrogen: 1.55,
E.oxygen: 1.5,
E.fluorine: 1.5,
E.silicon: 2.1,
E.phosphorus: 1.85,
E.sulfur: 1.8,
E.chlorine: 1.5,
}
if _have_numpy:
radii = numpy.empty(topology.getNumAtoms(), numpy.double)
else:
radii = [0]*topology.getNumAtoms()
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
if atom.element is E.carbon: radii[i] = element_to_radius.get(atom.element, default_radius)
radii[i] = 1.7
elif atom.element in (E.hydrogen, E.deuterium):
radii[i] = 1.2
elif atom.element is E.nitrogen:
radii[i] = 1.55
elif atom.element is E.oxygen:
radii[i] = 1.5
elif atom.element is E.fluorine:
radii[i] = 1.5
elif atom.element is E.silicon:
radii[i] = 2.1
elif atom.element is E.phosphorus:
radii[i] = 1.85
elif atom.element is E.sulfur:
radii[i] = 1.8
elif atom.element is E.chlorine:
radii[i] = 1.5
else:
radii[i] = 1.5
return radii # converted to nanometers above return radii # converted to nanometers above
def _mbondi_radii(topology): def _mbondi_radii(topology, all_bonds = None):
""" Sets the mbondi radii """ """ Sets the mbondi radii """
radii = [0.0 for atom in topology.atoms()] default_radius = 1.5
all_bonds = _get_bonded_atom_list(topology) element_to_const_radius = {
E.nitrogen: 1.55,
E.oxygen: 1.5,
E.fluorine: 1.5,
E.silicon: 2.1,
E.phosphorus: 1.85,
E.sulfur: 1.8,
E.chlorine: 1.5,
}
if _have_numpy:
radii = numpy.empty(topology.getNumAtoms(), numpy.double)
else:
radii = [0]*topology.getNumAtoms()
if all_bonds is None:
all_bonds = _get_bonded_atom_list(topology)
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
# Radius of H atom depends on element it is bonded to # Radius of H atom depends on element it is bonded to
if atom.element in (E.hydrogen, E.deuterium): element = atom.element
if element in (E.hydrogen, E.deuterium):
bondeds = all_bonds[atom] bondeds = all_bonds[atom]
if bondeds[0].element in (E.carbon, E.nitrogen): if bondeds[0].element in (E.carbon, E.nitrogen):
radii[i] = 1.3 radii[i] = 1.3
...@@ -272,67 +292,55 @@ def _mbondi_radii(topology): ...@@ -272,67 +292,55 @@ def _mbondi_radii(topology):
else: else:
radii[i] = 1.2 radii[i] = 1.2
# Radius of C atom depends on what type it is # Radius of C atom depends on what type it is
elif atom.element is E.carbon: elif element is E.carbon:
radii[i] = 1.7 radii[i] = 1.7
# All other elements have fixed radii for all types/partners # All other elements have fixed radii for all types/partners
elif atom.element is E.nitrogen:
radii[i] = 1.55
elif atom.element is E.oxygen:
radii[i] = 1.5
elif atom.element is E.fluorine:
radii[i] = 1.5
elif atom.element is E.silicon:
radii[i] = 2.1
elif atom.element is E.phosphorus:
radii[i] = 1.85
elif atom.element is E.sulfur:
radii[i] = 1.8
elif atom.element is E.chlorine:
radii[i] = 1.5
else: else:
radii[i] = 1.5 radii[i] = element_to_const_radius.get(element, default_radius)
return radii # converted to nanometers above return radii # converted to nanometers above
def _mbondi2_radii(topology): def _mbondi2_radii(topology, all_bonds = None):
""" Sets the mbondi2 radii """ """ Sets the mbondi2 radii """
radii = [0.0 for atom in topology.atoms()] default_radius = 1.5
all_bonds = _get_bonded_atom_list(topology) element_to_const_radius = {
E.nitrogen: 1.55,
E.oxygen: 1.5,
E.fluorine: 1.5,
E.silicon: 2.1,
E.phosphorus: 1.85,
E.sulfur: 1.8,
E.chlorine: 1.5,
}
if _have_numpy:
radii = numpy.empty(topology.getNumAtoms(), numpy.double)
else:
radii = [0]*topology.getNumAtoms()
if all_bonds is None:
all_bonds = _get_bonded_atom_list(topology)
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
element = atom.element
# Radius of H atom depends on element it is bonded to # Radius of H atom depends on element it is bonded to
if atom.element in (E.hydrogen, E.deuterium): if element in (E.hydrogen, E.deuterium):
bondeds = all_bonds[atom] bondeds = all_bonds[atom]
if bondeds[0].element is E.nitrogen: if bondeds[0].element is E.nitrogen:
radii[i] = 1.3 radii[i] = 1.3
else: else:
radii[i] = 1.2 radii[i] = 1.2
# Radius of C atom depends on what type it is # Radius of C atom depends on what type it is
elif atom.element is E.carbon: elif element is E.carbon:
radii[i] = 1.7 radii[i] = 1.7
# All other elements have fixed radii for all types/partners # All other elements have fixed radii for all types/partners
elif atom.element is E.nitrogen:
radii[i] = 1.55
elif atom.element is E.oxygen:
radii[i] = 1.5
elif atom.element is E.fluorine:
radii[i] = 1.5
elif atom.element is E.silicon:
radii[i] = 2.1
elif atom.element == E.phosphorus:
radii[i] = 1.85
elif atom.element == E.sulfur:
radii[i] = 1.8
elif atom.element == E.chlorine:
radii[i] = 1.5
else: else:
radii[i] = 1.5 radii[i] = element_to_const_radius.get(element, default_radius)
return radii # Converted to nanometers above return radii # Converted to nanometers above
def _mbondi3_radii(topology): def _mbondi3_radii(topology, all_bonds = None):
""" Sets the mbondi3 radii """ """ Sets the mbondi3 radii """
radii = _mbondi2_radii(topology) if all_bonds is None:
all_bonds = _get_bonded_atom_list(topology) all_bonds = _get_bonded_atom_list(topology)
radii = _mbondi2_radii(topology, all_bonds=all_bonds)
for i, atom in enumerate(topology.atoms()): for i, atom in enumerate(topology.atoms()):
# carboxylate and HH/HE (ARG) # carboxylate and HH/HE (ARG)
if _is_carboxylateO(atom, all_bonds): if _is_carboxylateO(atom, all_bonds):
...@@ -391,12 +399,10 @@ _SCREEN_PARAMETERS = { # normal, GBn, GBn2 ...@@ -391,12 +399,10 @@ _SCREEN_PARAMETERS = { # normal, GBn, GBn2
E.sulfur : (0.96, 0.602256336067, -0.703469), E.sulfur : (0.96, 0.602256336067, -0.703469),
None : (0.8, 0.5, 0.5) # default None : (0.8, 0.5, 0.5) # default
} }
_SCREEN_PARAMETERS[E.deuterium] = _SCREEN_PARAMETERS[E.hydrogen] _SCREEN_PARAMETERS[E.deuterium] = _SCREEN_PARAMETERS[E.hydrogen]
def _screen_parameter(atom): def _screen_parameter(atom):
if atom.element in _SCREEN_PARAMETERS: return _SCREEN_PARAMETERS.get(atom.element, _SCREEN_PARAMETERS[None])
return _SCREEN_PARAMETERS[atom.element]
return _SCREEN_PARAMETERS[None]
class CustomAmberGBForceBase(CustomGBForce): class CustomAmberGBForceBase(CustomGBForce):
...@@ -439,6 +445,39 @@ class CustomAmberGBForceBase(CustomGBForce): ...@@ -439,6 +445,39 @@ class CustomAmberGBForceBase(CustomGBForce):
self.parameters.append(params) self.parameters.append(params)
return params return params
def addParticles(self, params):
"""Add a set of particles to the force
Particles are added in order. The total number of particles added to the
force must match the number of particles in the system.
Parameters
----------
params : list or numpy array
A (natoms * npar) dimensional array of parameters to add to the
force. The meaning of the parameters depends on the model. All
parameters should be simple floating-point values in OpenMM's
default units.
Returns
-------
None
"""
if isinstance(params, list) and _have_numpy:
params = numpy.array(params)
else:
params = copy.deepcopy(params)
if _have_numpy:
params[:,self.RADIUS_ARG_POSITION] -= self.OFFSET
params[:,self.SCREEN_POSITION] *= params[:,self.RADIUS_ARG_POSITION]
self.parameters.extend(params.tolist())
else:
for p in params:
p[self.RADIUS_ARG_POSITION] -= self.OFFSET
p[self.SCREEN_POSITION] *= p[self.RADIUS_ARG_POSITION]
self.parameters.extend(params)
@staticmethod @staticmethod
def getStandardParameters(topology): def getStandardParameters(topology):
""" Gets list of standard parameters for this GB model based on an input Topology """ Gets list of standard parameters for this GB model based on an input Topology
...@@ -528,9 +567,13 @@ class GBSAHCTForce(CustomAmberGBForceBase): ...@@ -528,9 +567,13 @@ class GBSAHCTForce(CustomAmberGBForceBase):
at the beginning of the list at the beginning of the list
""" """
radii = [[x/10] for x in _mbondi_radii(topology)] if _have_numpy:
for i, atom in enumerate(topology.atoms()): radii = numpy.empty((topology.getNumAtoms(), 2), numpy.double)
radii[i].append(_screen_parameter(atom)[0]) radii[:,0] = _mbondi_radii(topology)/10
else:
radii = [[r/10, 0] for r in _mbondi_radii(topology)]
for rad, atom in zip(radii, topology.atoms()):
rad[1] = _screen_parameter(atom)[0]
return radii return radii
...@@ -590,9 +633,13 @@ class GBSAOBC1Force(CustomAmberGBForceBase): ...@@ -590,9 +633,13 @@ class GBSAOBC1Force(CustomAmberGBForceBase):
at the beginning of the list at the beginning of the list
""" """
radii = [[x/10] for x in _mbondi2_radii(topology)] if _have_numpy:
for i, atom in enumerate(topology.atoms()): radii = numpy.empty((topology.getNumAtoms(), 2), numpy.double)
radii[i].append(_screen_parameter(atom)[0]) radii[:,0] = _mbondi2_radii(topology)/10
else:
radii = [[r/10, 0] for r in _mbondi2_radii(topology)]
for rad, atom in zip(radii, topology.atoms()):
rad[1] = _screen_parameter(atom)[0]
return radii return radii
...@@ -678,6 +725,21 @@ class GBSAGBnForce(CustomAmberGBForceBase): ...@@ -678,6 +725,21 @@ class GBSAGBnForce(CustomAmberGBForceBase):
if parameters[1] + self.OFFSET < 0.1 or parameters[1] + self.OFFSET > 0.2: if parameters[1] + self.OFFSET < 0.1 or parameters[1] + self.OFFSET > 0.2:
raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup') raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup')
def addParticles(self, parameters):
bad_radius = False
if _have_numpy:
if isinstance(parameters, list):
parameters = numpy.array(parameters)
radii = parameters[:,1]
if numpy.any(numpy.logical_or(radii<0.1, radii>0.2)):
bad_radius = True
else:
if any(p[1]<0.1 or p[1]>0.2 for p in parameters):
bad_radius = True
if bad_radius:
raise ValueError('Radii must be between 1 and 2 Angstroms for neck lookup')
CustomAmberGBForceBase.addParticles(self, parameters)
def finalize(self): def finalize(self):
self._findUniqueRadii() self._findUniqueRadii()
self._createRadiusToIndexMap() self._createRadiusToIndexMap()
...@@ -701,9 +763,13 @@ class GBSAGBnForce(CustomAmberGBForceBase): ...@@ -701,9 +763,13 @@ class GBSAGBnForce(CustomAmberGBForceBase):
at the beginning of the list at the beginning of the list
""" """
radii = [[x/10] for x in _bondi_radii(topology)] if _have_numpy:
for i, atom in enumerate(topology.atoms()): radii = numpy.empty((topology.getNumAtoms(), 2), numpy.double)
radii[i].append(_screen_parameter(atom)[1]) radii[:,0] = _bondi_radii(topology)/10
else:
radii = [[r/10, 0] for r in _bondi_radii(topology)]
for rad, atom in zip(radii, topology.atoms()):
rad[1] = _screen_parameter(atom)[1]
return radii return radii
def _findUniqueRadii(self): def _findUniqueRadii(self):
...@@ -799,8 +865,18 @@ class GBSAGBn2Force(GBSAGBnForce): ...@@ -799,8 +865,18 @@ class GBSAGBn2Force(GBSAGBnForce):
def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None, kappa=0.0): def __init__(self, solventDielectric=78.5, soluteDielectric=1, SA=None, cutoff=None, kappa=0.0):
GBSAGBnForce.__init__(self, solventDielectric, soluteDielectric, SA, cutoff, kappa) GBSAGBnForce.__init__(self, solventDielectric, soluteDielectric, SA, cutoff, kappa)
@staticmethod _atom_params = {
def getStandardParameters(topology): E.hydrogen: [0.788440, 0.798699, 0.437334],
E.deuterium: [0.788440, 0.798699, 0.437334],
E.carbon: [0.733756, 0.506378, 0.205844],
E.nitrogen: [0.503364, 0.316828, 0.192915],
E.oxygen: [0.867814, 0.876635, 0.387882],
E.sulfur: [0.867814, 0.876635, 0.387882],
}
_default_atom_params = [0.8, 4.85, 0.5]
@classmethod
def getStandardParameters(cls, topology):
""" Gets list of standard parameters for this GB model based on an input Topology """ Gets list of standard parameters for this GB model based on an input Topology
Parameters Parameters
...@@ -816,21 +892,19 @@ class GBSAGBn2Force(GBSAGBnForce): ...@@ -816,21 +892,19 @@ class GBSAGBn2Force(GBSAGBnForce):
at the beginning of the list at the beginning of the list
""" """
radii = [[x/10] for x in _mbondi3_radii(topology)] natoms = topology.getNumAtoms()
for i, atom in enumerate(topology.atoms()): if _have_numpy:
radii[i].append(_screen_parameter(atom)[2]) radii = numpy.empty([natoms,5], numpy.double)
if atom.element in (E.hydrogen, E.deuterium): radii[:,0] = _mbondi3_radii(topology)/10
radii[i].extend([0.788440, 0.798699, 0.437334]) for atom, rad in zip(topology.atoms(), radii):
elif atom.element is E.carbon: rad[1] = _screen_parameter(atom)[2]
radii[i].extend([0.733756, 0.506378, 0.205844]) rad[2:] = cls._atom_params.get(atom.element, cls._default_atom_params)
elif atom.element is E.nitrogen: else:
radii[i].extend([0.503364, 0.316828, 0.192915]) radii = [[r/10, 0, 0, 0, 0] for r in _mbondi3_radii(topology)]
elif atom.element is E.oxygen: for atom, rad in zip(topology.atoms(), radii):
radii[i].extend([0.867814, 0.876635, 0.387882]) rad[1] = _screen_parameter(atom)[2]
elif atom.element is E.sulfur: for i, p in enumerate(cls._atom_params.get(atom.element, cls._default_atom_params)):
radii[i].extend([0.867814, 0.876635, 0.387882]) rad[2+i] = p
else:
radii[i].extend([0.8, 4.85, 0.5])
return radii return radii
def _addEnergyTerms(self): def _addEnergyTerms(self):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment