Restoring integrators from CompoundIntegrators prevents resets
Example below, where my LangevinIntegrator contains the boolean for ._meaure_heat. I then pass it into a CompoundIntegrator and attempt to reset the LangevinIntegrator and get an error saying that it is missing the property.
from simtk.openmm import CompoundIntegrator
from openmmtools.utils import RestorableOpenMMObject
from openmmtools.integrators import *
temperature = 300*unit.kelvin
collision_rate = 1/unit.picoseconds
timestep = 1.0*unit.femtoseconds
n_steps = 100
alchemical_functions = {'lambda_sterics' : 'lambda'}
ncmc_integrator = AlchemicalNonequilibriumLangevinIntegrator(alchemical_functions,
splitting='O { V R H R V } O',
temperature=temperature,
collision_rate=collision_rate,
timestep=timestep,
nsteps_neq=n_steps,
measure_heat=True)
integrator = LangevinIntegrator(temperature=temperature,
timestep=timestep,
collision_rate=collision_rate,
measure_heat=True)
print(integrator)
print(integrator._measure_heat)
compound_integrator = openmm.CompoundIntegrator()
compound_integrator.addIntegrator(ncmc_integrator)
compound_integrator.addIntegrator(integrator)
print(compound_integrator)
langevin_integrator = compound_integrator.getIntegrator(1)
RestorableOpenMMObject.restore_interface(langevin_integrator)
print(langevin_integrator)
langevin_integrator.reset()
#print(langevin_integrator._measure_heat)
Error below
>>> <openmmtools.integrators.LangevinIntegrator; proxy of <Swig Object of type 'OpenMM::CustomIntegrator *' at 0x15182b6480> >
>>> True
>>> <simtk.openmm.openmm.CompoundIntegrator; proxy of <Swig Object of type 'OpenMM::CompoundIntegrator *' at 0x15182e4060> >
>>> <openmmtools.integrators.LangevinIntegrator; proxy of <Swig Object of type 'OpenMM::CustomIntegrator *' at 0x15182e4600> >
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-6-9c345ca3462e> in <module>()
30
31 print(langevin_integrator)
---> 32 langevin_integrator.reset()
33 #print(langevin_integrator._measure_heat)
~/Research/Github/molssi-project/openmmtools/openmmtools/integrators.py in reset(self)
1186 """Reset all statistics (heat, shadow work, acceptance rates, step).
1187 """
-> 1188 self.reset_heat()
1189 self.reset_shadow_work()
1190 self.reset_ghmc_statistics()
~/Research/Github/molssi-project/openmmtools/openmmtools/integrators.py in reset_heat(self)
1163 def reset_heat(self):
1164 """Reset heat."""
-> 1165 if self._measure_heat:
1166 self.setGlobalVariableByName('heat', 0.0)
1167
//anaconda/envs/molssi/lib/python3.5/site-packages/simtk/openmm/openmm.py in <lambda>(self, name)
10724 for _s in [Integrator]:
10725 __swig_getmethods__.update(getattr(_s, '__swig_getmethods__', {}))
> 10726 __getattr__ = lambda self, name: _swig_getattr(self, CustomIntegrator, name)
10727 __repr__ = _swig_repr
10728 ComputeGlobal = _openmm.CustomIntegrator_ComputeGlobal
//anaconda/envs/molssi/lib/python3.5/site-packages/simtk/openmm/openmm.py in _swig_getattr(self, class_type, name)
72
73 def _swig_getattr(self, class_type, name):
---> 74 return _swig_getattr_nondynamic(self, class_type, name, 0)
75
76
//anaconda/envs/molssi/lib/python3.5/site-packages/simtk/openmm/openmm.py in _swig_getattr_nondynamic(self, class_type, name, static)
67 return method(self)
68 if (not static):
---> 69 return object.__getattr__(self, name)
70 else:
71 raise AttributeError(name)
AttributeError: type object 'object' has no attribute '__getattr__'
I can attempt to fix it, if someone could provide any pointers on where to start poking around?
Looking at the error, I believe that the problem is that CompoundIntegrator.getIntegrator() does not preserve the Python class. I think Peter fixed it for Context.getIntegrator(), but apparently those changes did not affect CompoundIntegrator. Which OpenMM version are you using?
In any case, I'm all in favor of removing all pure-Python attributes from our integrator classes in favor of properties that read global variables since, as you noticed, including pure-Python attributes makes it impossible to reconstruct the integrator from its OpenMM XML serialization.
For example, changing LangevinIntegrator._measure_heat from a simple attribute to a property like
@property
def _measure_heat(self):
try:
self.getGlobalVariableByName('heat')
except:
return False
return True
See also this section of the new tutorial (it still has to be pushed to the online docs but it should be already readable from github even if it uses RST syntax).
Or maybe even better, removing _measure_heat completely and just implement get/set_heat (and other variables related to shadow work etc.) with something like
def get_heat(self):
try:
return self.getGlobalVariableByName('heat')
except:
raise Exception("This integrator must be constructed with 'measure_heat=True' in order to measure heat.")
Looking at the error, I believe that the problem is that CompoundIntegrator.getIntegrator() does not preserve the Python class. I think Peter fixed it for Context.getIntegrator(), but apparently those changes did not affect CompoundIntegrator.
Can we make sure to report this to the OpenMM issue tracker if that needs to be fixed? There's still time to fix it in 7.3!
I'll open the issue over there. It would still be a good idea to change the implementations though so that it would be possible to read OpenMM XML files and send our custom integrators over the network.
Issue opened: pandegroup/openmm#2168 .
See Peter's comment on the issue tracker. He did try to make it work in the past, but it's more complicated than it appears so the only way we have to fix this problem (and also support correctly XML deserialization) is to change the integrator's implementation to remove the Python attributes.