Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GlobalParameterState and changes for multi-restraint calculations #363

Merged
merged 35 commits into from
Sep 17, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
41a3145
Fix typo
andrrizzi Jul 21, 2018
e68add2
Allow restraint forces to be controlled by an arbitrary parameter.
andrrizzi Jul 21, 2018
37fd7da
Require old_attribute_value argument to IComposableState._on_setattr …
andrrizzi Jul 21, 2018
a440ea2
Fix signature AlchemicalState._on_setattr.
andrrizzi Jul 21, 2018
2377556
Deprecate update_alchemical_charges argument of AlchemicalState.
andrrizzi Jul 21, 2018
d20d7b3
Implement GlobalParameterState facility.
andrrizzi Jul 21, 2018
84b3d85
Bump minor version: 0.16.0
andrrizzi Jul 21, 2018
e7c825c
Update docs and release history
andrrizzi Jul 21, 2018
1c575a0
Merge branch 'master' into multi-restraints
andrrizzi Jul 23, 2018
004bbb9
Do not raise deprecation warning in from_system if not necessary.
andrrizzi Jul 24, 2018
694f66d
Fix doctests
andrrizzi Jul 24, 2018
32ed63a
Merge branch 'multi-restraints' of github.com:choderalab/openmmtools …
andrrizzi Jul 24, 2018
b0f219e
Fix tests
andrrizzi Jul 24, 2018
1046305
Python 2 compatibility fixes
andrrizzi Jul 24, 2018
8ed7277
Wrap energy function in parenthesis before multiplying by controlling…
andrrizzi Jul 25, 2018
e803f50
Remove check which ignored test because openmm7.2 is actually out
Lnaden Jul 26, 2018
05f1bdc
Convert HarmonicRestraint Forces to CV Forces
Lnaden Jul 31, 2018
fdc41d5
Fix typo in meta.yaml
Lnaden Jul 31, 2018
8726101
Make it way more clear about backwards compatibility breaking.
Lnaden Jul 31, 2018
e36b593
Start trying to make serialization more stable.
Lnaden Aug 1, 2018
66d86c6
Update the forces to be restorable correctly.
Lnaden Aug 1, 2018
3c7ba04
Fix unneeded import
Lnaden Aug 1, 2018
7686d1f
Stupid. netcdf. stupid
Lnaden Aug 1, 2018
77c6ac8
AHG!
Lnaden Aug 1, 2018
24841a0
Merge pull request #365 from choderalab/cv-restraints
Lnaden Aug 2, 2018
aa3d00f
Pin to the last build which worked for us
Lnaden Aug 2, 2018
f8d6ed8
Overkill build pin of netcdf4
Lnaden Aug 2, 2018
b96e28f
Running out of things to pin for windows here...
Lnaden Aug 2, 2018
5c6a0aa
I'm giving up after this
Lnaden Aug 2, 2018
c548fbf
I'm giving up after this, maybe
Lnaden Aug 2, 2018
b64c7c5
Pass parameter_name_suffix as positional argument to allow renaming i…
andrrizzi Aug 2, 2018
e0afad7
Testing a different workaround for the NetCDF issues
Lnaden Aug 8, 2018
bf6e799
Revert changes from PR #365. These will be re-added in a separate PR.
andrrizzi Sep 17, 2018
8e14077
Re-add some of the fixes that are necessary to merge this PR
andrrizzi Sep 17, 2018
f28b16a
Merge branch 'master' into multi-restraints
andrrizzi Sep 17, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions docs/releasehistory.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
Release History
===============

0.15.1 - Something New This Way Comes [WIP]
===========================================
0.16.0 - GlobalParameterState class, SamplerState reads CVs
============================================================

New features
------------
- Add ability for ``SamplerState`` to access new `OpenMM Custom CV Force Variables <http://docs.openmm.org/development/api-python/generated/simtk.openmm.openmm.CustomCVForce.html#simtk.openmm.openmm.CustomCVForce>`_
- ``SamplerState.update_from_context`` now has keywords to support finer grain updating from the Context. This is only recommended for advanced users.
- Add ability for ``SamplerState`` to access new `OpenMM Custom CV Force Variables
<http://docs.openmm.org/development/api-python/generated/simtk.openmm.openmm.CustomCVForce.html#simtk.openmm.openmm.CustomCVForce>`_
(`#362 <https://github.com/choderalab/openmmtools/pull/362>`_).
- ``SamplerState.update_from_context`` now has keywords to support finer grain updating from the Context. This is only
recommended for advanced users (`#362 <https://github.com/choderalab/openmmtools/pull/362>`_).
- Added the new class ``states.GlobalParameterState`` designed to simplify the implementation of composable states that
control global variables (`#363 <https://github.com/choderalab/openmmtools/pull/363>`_).
- Allow restraint force classes to be controlled by a parameter other than ``lambda_restraints``. This will enable
multi-restraints simulations (`#363 <https://github.com/choderalab/openmmtools/pull/363>`_).

Deprecated
----------
- Python2 is officially deprecated. Support will be dropped in future versions.
- Deprecated the signature of ``IComposableState._on_setattr`` to fix a bug where the objects were temporarily left in
an inconsistent state when an exception was raised and caught.
- Deprecated ``update_alchemical_charges`` in ``AlchemicalState`` in anticipation of the new implementation of the
exact PME that will be based on the ``NonbondedForce`` offsets rather than ``updateParametersInContext()``.


0.15.0 - Restraint forces
=========================
Expand Down
2 changes: 2 additions & 0 deletions docs/states.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The module :mod:`openmmtools.states` contains classes to maintain a consistent s
- :class:`ThermodynamicState`: Represent and manipulate the thermodynamic state of OpenMM `System <http://docs.openmm.org/7.1.0/api-python/generated/simtk.openmm.openmm.System.html>`_ and `Context <http://docs.openmm.org/7.1.0/api-python/generated/simtk.openmm.openmm.Context.html>`_ objects.
- :class:`SamplerState`: Represent and cache the state of the simulation that changes when the `System <http://docs.openmm.org/7.1.0/api-python/generated/simtk.openmm.openmm.System.html>`_ is integrated.
- :class:`CompoundThermodynamicState`: Extend the :class:`ThermodynamicState` to handle parameters other than temperature and pressure through the implementations of the :class:`IComposableState` abstract class.
- :class:`GlobalParameterState`: An implementation of :class:`IComposableState` specialized to control OpenMM forces' global parameters.

States
------
Expand All @@ -21,3 +22,4 @@ States
SamplerState
CompoundThermodynamicState
IComposableState
GlobalParameterState
26 changes: 19 additions & 7 deletions openmmtools/alchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class AlchemicalState(object):
lambda_torsions : float, optional
Scaling factor for alchemically-softened torsions (default is 1.0).
update_alchemical_charges : bool, optional
If True, ``lambda_electrostatics`` changes in alchemical systems
If False, ``lambda_electrostatics`` changes in alchemical systems
that use exact treatment of PME electrostatics will be considered
incompatible. This means that a new ``Context`` will be required
for each `lambda_electrostatics`` state.
Expand Down Expand Up @@ -211,6 +211,12 @@ class AlchemicalState(object):
# -------------------------------------------------------------------------

def __init__(self, **kwargs):
if 'update_alchemical_charges' in kwargs:
# TODO Drop support for the parameter and remove deprecation warning from 0.17 on
# TODO after implementing new way for exact PME based on the NonbondedForce offsets.
import warnings
warnings.warn('The update_alchemical_charges in AlchemicalState.__init__ has been '
'deprecated, and future versions of openmmtools may not support it.')
self._initialize(**kwargs)

@classmethod
Expand Down Expand Up @@ -251,16 +257,19 @@ def from_system(cls, system):
alchemical_parameters[parameter_name] = parameter_value

# Handle the update parameters flag.
update_alchemical_charges = bool(alchemical_parameters.pop(_UPDATE_ALCHEMICAL_CHARGES_PARAMETER,
cls._UPDATE_ALCHEMICAL_CHARGES_DEFAULT))
update_alchemical_charges = alchemical_parameters.pop(_UPDATE_ALCHEMICAL_CHARGES_PARAMETER, None)

# Check that the system is alchemical.
if len(alchemical_parameters) == 0:
raise AlchemicalStateError('System has no lambda parameters.')

# Avoid passing update_alchemical_charges if not necessary to not raise deprecation warning.
if (update_alchemical_charges is not None and
update_alchemical_charges != cls._UPDATE_ALCHEMICAL_CHARGES_DEFAULT):
alchemical_parameters['update_alchemical_charges'] = bool(update_alchemical_charges)

# Create and return the AlchemicalState.
return AlchemicalState(update_alchemical_charges=update_alchemical_charges,
**alchemical_parameters)
return AlchemicalState(**alchemical_parameters)

# -------------------------------------------------------------------------
# Lambda properties
Expand Down Expand Up @@ -392,7 +401,8 @@ def __setstate__(self, serialization):
parameters = serialization['parameters']
alchemical_variables = serialization['alchemical_variables']
# New attribute in OpenMMTools 0.14.0.
update_alchemical_charges = serialization.get('update_alchemical_charges', True)
update_alchemical_charges = serialization.get('update_alchemical_charges',
self._UPDATE_ALCHEMICAL_CHARGES_DEFAULT)
alchemical_functions = dict()

# Temporarily store alchemical functions.
Expand Down Expand Up @@ -554,7 +564,7 @@ def _standardize_system(self, system, set_lambda_electrostatics=False):
# states with different settings must be incompatible.
alchemical_state._apply_to_system(system, set_update_charges_flag=False)

def _on_setattr(self, standard_system, attribute_name):
def _on_setattr(self, standard_system, attribute_name, old_attribute_value):
"""Check if the standard system needs changes after a state attribute is set.

Parameters
Expand All @@ -563,6 +573,8 @@ def _on_setattr(self, standard_system, attribute_name):
The standard system before setting the attribute.
attribute_name : str
The name of the attribute that has just been set or retrieved.
old_attribute_value : float
The value of the attribute retrieved before being set.

Returns
-------
Expand Down
2 changes: 1 addition & 1 deletion openmmtools/forcefactories.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def replace_reaction_field(reference_system, switch_width=1.0*unit.angstrom,
Setting it to `False` speeds up the function execution but modifies
the `reference_system` object.
shifted : bool, optional, default=False
If `True`, a shited reaction-field will be used.
If `True`, a shifted reaction-field will be used.

Returns
-------
Expand Down
75 changes: 55 additions & 20 deletions openmmtools/forces.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,22 @@ class RadiallySymmetricRestraintForce(utils.RestorableOpenMMObject):
The indices of the first group of atoms to restrain.
restrained_atom_indices2 : iterable of int
The indices of the second group of atoms to restrain.
controlling_parameter_name : str
The name of the global parameter controlling the energy function.
*args, **kwargs
Parameters to pass to the super constructor.

Attributes
----------
controlling_parameter_name

"""

def __init__(self, restraint_parameters, restrained_atom_indices1,
restrained_atom_indices2, *args, **kwargs):
restrained_atom_indices2, controlling_parameter_name,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make controlling_parameter_name have a default? E.g. controlling_parameter_name = 'lambda_restraints'? Or would we instead just want controlling_parameter_name = 'restraints' and the lambda_ prefix is assumed prepended?

*args, **kwargs):
super(RadiallySymmetricRestraintForce, self).__init__(*args, **kwargs)
self._controlling_parameter_name = controlling_parameter_name

# Unzip bond parameters names and values from dict.
assert len(restraint_parameters) == 1 or isinstance(restraint_parameters, collections.OrderedDict)
Expand All @@ -271,7 +279,7 @@ def __init__(self, restraint_parameters, restrained_atom_indices1,
self._create_bond(parameter_values, restrained_atom_indices1, restrained_atom_indices2)

# Add parameters.
self.addGlobalParameter('lambda_restraints', 1.0)
self.addGlobalParameter(self._controlling_parameter_name, 1.0)
for parameter in parameter_names:
self.addPerBondParameter(parameter)

Expand Down Expand Up @@ -318,6 +326,11 @@ def restraint_parameters(self):
for parameter_idx, parameter_value in enumerate(parameter_values)]
return collections.OrderedDict(restraint_parameters)

@property
def controlling_parameter_name(self):
"""str: The name of the global parameter controlling the energy function (read-only)."""
return self._controlling_parameter_name

def distance_at_energy(self, potential_energy):
"""Compute the distance at which the potential energy is ``potential_energy``.

Expand Down Expand Up @@ -650,16 +663,17 @@ class RadiallySymmetricCentroidRestraintForce(RadiallySymmetricRestraintForce,

The restraint is applied between the centers of mass of two groups
of atoms. The restraint strength is controlled by a global context
parameter called 'lambda_restraints'.
parameter whose name is passed on construction through the optional
argument ``controlling_parameter_name``.

With OpenCL, only on 64bit platforms are supported.

Parameters
----------
energy_function : str
The energy function to pass to ``CustomCentroidBondForce``. The
global parameter 'lambda_restraint' will be prepended to this
expression.
name of the controlling global parameter will be prepended to
this expression.
restraint_parameters : OrderedDict
An ordered dictionary containing the bond parameters in the form
parameter_name: parameter_value. The order is important to make
Expand All @@ -669,23 +683,28 @@ class RadiallySymmetricCentroidRestraintForce(RadiallySymmetricRestraintForce,
The indices of the first group of atoms to restrain.
restrained_atom_indices2 : iterable of int
The indices of the second group of atoms to restrain.
controlling_parameter_name : str, optional
The name of the global parameter controlling the energy function.
The default value is 'lambda_restraints'.

Attributes
----------
restraint_parameters
restrained_atom_indices1
restrained_atom_indices2
controlling_parameter_name

"""

def __init__(self, energy_function, restraint_parameters,
restrained_atom_indices1, restrained_atom_indices2):
restrained_atom_indices1, restrained_atom_indices2,
controlling_parameter_name='lambda_restraints'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, see, here in this subclass, controlling_parameter_name an optional keyword arg, which changes the call from the parent function. We should make it consistent in the base class as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make it consistent in the base class as well

Sounds good! I'll move the default to the base class and use kwargs here. I'll have to keep it buried in kwargs in the base class though because Python 2 doesn't allow having both kwargs and keyword arguments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah... I forgot about that. Might be best to leave it then and it can be removed in 0.17 when we drop Py2 officially

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I just rememberd why I did this. It's because I need to use controlling_parameter_name in the subclass so I need the default to be specified there. I wouldn't write the default in two places as it's harder to keep track of (i.e., if a dev changes only one of the two the code may become inconsistent and unintuitive).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a good point

# Initialize CustomCentroidBondForce.
energy_function = 'lambda_restraints * ' + energy_function
energy_function = controlling_parameter_name + ' * ' + energy_function
custom_centroid_bond_force_args = [2, energy_function]
super(RadiallySymmetricCentroidRestraintForce, self).__init__(
restraint_parameters, restrained_atom_indices1, restrained_atom_indices2,
*custom_centroid_bond_force_args)
controlling_parameter_name, *custom_centroid_bond_force_args)

@property
def restrained_atom_indices1(self):
Expand Down Expand Up @@ -726,12 +745,14 @@ class RadiallySymmetricBondRestraintForce(RadiallySymmetricRestraintForce,
"""

def __init__(self, energy_function, restraint_parameters,
restrained_atom_index1, restrained_atom_index2):
restrained_atom_index1, restrained_atom_index2,
controlling_parameter_name='lambda_restraints'):
# Initialize CustomBondForce.
energy_function = energy_function.replace('distance(g1,g2)', 'r')
energy_function = 'lambda_restraints * ' + energy_function
super(RadiallySymmetricBondRestraintForce, self).__init__(restraint_parameters,
[restrained_atom_index1], [restrained_atom_index2], energy_function)
energy_function = controlling_parameter_name + ' * ' + energy_function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I just noticed a potential bug here. If the user passes in an energy_function which has additive terms and not wrapped in ( ), this could lead to unintended behavior, which would NOT throw an error. Off the top of my head, the only practical thing I can think of is some kind of external field or shifted function, but this is also an easy enough fix it may be worth fixing now.

e.g.

>>> energy_function = '(K/2)*r^2 + 5*r'
>>> controlling_parameter_name = 'X`
>>> controlling_parameter_name + ' * ' + energy_function
X * (K/2)*r^2 + 5*r

the 5*r term won't be scaled.

Easy fix is just controlling_parameter_name + ' * (' + energy_function + ')'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Will change it.

super(RadiallySymmetricBondRestraintForce, self).__init__(
restraint_parameters, [restrained_atom_index1], [restrained_atom_index2],
controlling_parameter_name, energy_function)

# -------------------------------------------------------------------------
# Public properties.
Expand Down Expand Up @@ -832,12 +853,11 @@ class HarmonicRestraintForce(HarmonicRestraintForceMixIn,

The energy expression of the restraint is given by

``E = lambda_restraints * (K/2)*r^2``
``E = controlling_parameter * (K/2)*r^2``

where `K` is the spring constant, `r` is the distance between the
two group centroids, and `lambda_restraints` is a scale factor that
can be used to control the strength of the restraint. You can control
``lambda_restraints`` through :class:`RestraintState` class.
two group centroids, and `controlling_parameter` is a scale factor that
can be used to control the strength of the restraint.

With OpenCL, only on 64bit platforms are supported.

Expand All @@ -850,13 +870,17 @@ class HarmonicRestraintForce(HarmonicRestraintForceMixIn,
The indices of the first group of atoms to restrain.
restrained_atom_indices2 : iterable of int
The indices of the second group of atoms to restrain.
controlling_parameter_name : str, optional
The name of the global parameter controlling the energy function.
The default value is 'lambda_restraints'.

Attributes
----------
spring_constant
restrained_atom_indices1
restrained_atom_indices2
restraint_parameters
controlling_parameter_name

"""
# All the methods are provided by the mix-ins.
Expand All @@ -879,13 +903,17 @@ class HarmonicRestraintBondForce(HarmonicRestraintForceMixIn,
The index of the first atom to restrain.
restrained_atom_index2 : int
The index of the second atom to restrain.
controlling_parameter_name : str, optional
The name of the global parameter controlling the energy function.
The default value is 'lambda_restraints'.

Attributes
----------
spring_constant
restrained_atom_indices1
restrained_atom_indices2
restraint_parameters
controlling_parameter_name

"""
# All the methods are provided by the mix-ins.
Expand Down Expand Up @@ -986,14 +1014,13 @@ class FlatBottomRestraintForce(FlatBottomRestraintForceMixIn,

More precisely, the energy expression of the restraint is given by

``E = lambda_restraints * step(r-r0) * (K/2)*(r-r0)^2``
``E = controlling_parameter * step(r-r0) * (K/2)*(r-r0)^2``

where ``K`` is the spring constant, ``r`` is the distance between the
restrained atoms, ``r0`` is another parameter defining the distance
at which the restraint is imposed, and ``lambda_restraints``
at which the restraint is imposed, and ``controlling_parameter``
is a scale factor that can be used to control the strength of the
restraint. You can control ``lambda_restraints`` through the class
:class:`RestraintState`.
restraint.

With OpenCL, only on 64bit platforms are supported.

Expand All @@ -1009,6 +1036,9 @@ class FlatBottomRestraintForce(FlatBottomRestraintForceMixIn,
The indices of the first group of atoms to restrain.
restrained_atom_indices2 : iterable of int
The indices of the second group of atoms to restrain.
controlling_parameter_name : str, optional
The name of the global parameter controlling the energy function.
The default value is 'lambda_restraints'.

Attributes
----------
Expand All @@ -1017,6 +1047,7 @@ class FlatBottomRestraintForce(FlatBottomRestraintForceMixIn,
restrained_atom_indices1
restrained_atom_indices2
restraint_parameters
controlling_parameter_name

"""
# All the methods are provided by the mix-ins.
Expand All @@ -1042,6 +1073,9 @@ class FlatBottomRestraintBondForce(FlatBottomRestraintForceMixIn,
The index of the first group of atoms to restrain.
restrained_atom_index2 : int
The index of the second group of atoms to restrain.
controlling_parameter_name : str, optional
The name of the global parameter controlling the energy function.
The default value is 'lambda_restraints'.

Attributes
----------
Expand All @@ -1050,6 +1084,7 @@ class FlatBottomRestraintBondForce(FlatBottomRestraintForceMixIn,
restrained_atom_indices1
restrained_atom_indices2
restraint_parameters
controlling_parameter_name

"""
# All the methods are provided by the mix-ins.
Expand Down
Loading