integrators¶
- class ufedmm.integrators.AbstractMiddleRespaIntegrator(temperature, step_size, num_rattles=0, scheme='VV-Middle', respa_loops=[1], bath_loops=1, intertwine=True, embodied_force_groups=[], unroll_loops=True)[source]¶
Bases:
CustomIntegrator
An abstract class for middle-type, multiple time-scale integrators.
Warning
This class is meant for inheritance only and does not actually include thermostatting. Concrete subclasses are available, such as
MiddleMassiveNHCIntegrator
andGeodesicLangevinIntegrator
, for instance.Child classes will differ by the thermostat algorithm, which must be implemented by overriding the _bath method (see the example below). Temperature is treated as a per-dof parameter so as to allow adiabatic simulations.
The following ODE system is solved for every degree of freedom in the system, with possibly \(n_c\) holonomic constraints and with forces possibly split into \(m\) parts according to their characteristic time scales:
\[\begin{split}& \dot{r}_i = v_i \\ & \dot{v}_i = \frac{\sum_{k=1}^m F_i^{[k]}}{m_i} + \sum_{k=1}^{n_c} \lambda_k \nabla_{r_i} \sigma_k + \mathrm{bath}(T_i, v_i) \\ & \sigma_k(\mathbf{r}) = 0\end{split}\]An approximate solution is obtained by applying the Trotter-Suzuki splitting formula. In the particular case of two time scales, the default splitting scheme goes as follows:
\[e^{\Delta t\mathcal{L}} = e^{\frac{\Delta t}{2}\mathcal{L}^{[1]}_v} \left[ e^{\frac{\Delta t}{2 n_0}\mathcal{L}^{[0]}_v} \left( e^{\frac{\Delta t}{2 n_0 n_b}\mathcal{L}_r} e^{\frac{\Delta t}{n_0 n_b}\mathcal{L}_\mathrm{bath}} e^{\frac{\Delta t}{2 n_0 n_b}\mathcal{L}_r} \right)^{n_b} e^{\frac{\Delta t}{2 n_0}\mathcal{L}^{[0]}_v} \right]^{n_0} e^{\frac{\Delta t}{2}\mathcal{L}^{[1]}_v}\]Each exponential operator is the solution of a particular subsystem of equations. If \(\mathrm{bath}(T_i, v_i) = 0\), the scheme above is time-reversible, measure-preserving, and symplectic. It is referred to as the
VV-Middle
scheme [2], where VV stands for Velocity Verlet. An alternative approach is also available, which is:\[e^{\Delta t\mathcal{L}} = \left[ \left( e^{\frac{\Delta t}{2 n_0 n_b}\mathcal{L}_r} e^{\frac{\Delta t}{n_0 n_b}\mathcal{L}_\mathrm{bath}} e^{\frac{\Delta t}{2 n_0 n_b}\mathcal{L}_r} \right)^{n_b} e^{\frac{\Delta t}{n_0}\mathcal{L}^{[0]}_v} \right]^{n_0} e^{\Delta t \mathcal{L}^{[1]}_v}\]This is referred to as the
LF-Middle
scheme [2], where LF stands for Leap-Frog. In contrast to the previous scheme, it is not time-reversible. However, in single time-scale simulations, the two approaches result in equivalent coordinate trajectories, while the latter provides a velocity trajectory more consistent with the Maxwell-Boltzmann distribution at the specified temperature [2].- Parameters:
temperature (unit.Quantity) – The temperature of the heat bath.
step_size (float or unit.Quantity) – The outer step size with which to integrate the equations of motion.
- Keyword Arguments:
num_rattles (int, default=0) – The number of RATTLE computations for geodesic integration [3]. If
num_rattles=0
, then no constraints are considered at all.scheme (str, default='VV-Middle') – Which splitting scheme will be used. Valid options are ‘VV-Middle’ and ‘LF-Middle’.
respa_loops (list(int), default=[1]) – A list of m integers, where respa_loops[k] determines how many substeps with force group k are internally executed for every step with force group k+1.
bath_loops (int, default=1) – The number of iterations of the bath operator per each step at time scale 0. This is useful when the bath operator is not exact, but derived from a splitting solution.
embodied_force_groups (list(int), default=[]) – A list of indices of force groups. The presence of an index i is this list means that the contribution of force group i is embodied in force group i+1. Therefore, such contribution must be properly subtracted during the integration at time scale i+1. This feature requires OpenMM 7.5 or a newer version.
unroll_loops (bool, default=True) – Whether the integrator loops should be unrolled for improving efficiency. Using
unroll_loops=False
can be useful for printing the integrator steps.
Example
>>> from ufedmm import integrators >>> from openmm import unit >>> class MiddleNoseHooverIntegrator(integrators.AbstractMiddleRespaIntegrator): ... def __init__(self, ndof, tau, temperature, step_size, num_rattles=1): ... super().__init__( ... temperature, step_size, num_rattles, 'VV-Middle', [1], 1 ... ) ... kB = 8.3144626E-3*unit.kilojoules_per_mole/unit.kelvin ... gkT = ndof*unit.MOLAR_GAS_CONSTANT_R*temperature ... self.addGlobalVariable('gkT', gkT) ... self.addGlobalVariable('Q', gkT*tau**2) ... self.addGlobalVariable('v_eta', 0) ... self.addGlobalVariable('twoK', 0) ... self.addGlobalVariable('scaling', 1) ... def _bath(self, fraction): ... self.addComputeSum('twoK', 'm*v*v') ... self.addComputeGlobal( ... 'v_eta', f'v_eta + {0.5*fraction}*dt*(twoK - gkT)/Q' ... ) ... self.addComputeGlobal('scaling', f'exp(-{fraction}*dt*v_eta)') ... self.addComputePerDof('v', f'v*scaling') ... self.addComputeGlobal( ... 'v_eta', f'v_eta + {0.5*fraction}*dt*(scaling^2*twoK - gkT)/Q' ... ) >>> integrator = MiddleNoseHooverIntegrator( ... 500, 10*unit.femtoseconds, 300*unit.kelvin, ... 1*unit.femtoseconds, num_rattles=0 ... ) >>> print(integrator) Per-dof variables: kT Global variables: gkT = 1247.169392722986 Q = 0.1247169392722986 v_eta = 0.0 twoK = 0.0 scaling = 1.0 Computation steps: 0: allow forces to update the context state 1: v <- v + 0.5*dt*f/m 2: x <- x + 0.5*dt*v 3: twoK <- sum(m*v*v) 4: v_eta <- v_eta + 0.5*dt*(twoK - gkT)/Q 5: scaling <- exp(-1.0*dt*v_eta) 6: v <- v*scaling 7: v_eta <- v_eta + 0.5*dt*(scaling^2*twoK - gkT)/Q 8: x <- x + 0.5*dt*v 9: v <- v + 0.5*dt*f/m
- class ufedmm.integrators.CustomIntegrator(temperature, step_size)[source]¶
Bases:
CustomIntegrator
An extension of openmm.CustomIntegrator class with an extra per-dof variable named temperature, whose content is the temperature of the heat bath associated to each degree of freedom. A per-dof temperature is necessary if the extended-space variables and the physical system are coupled adiabatically to thermostats at different temperatures. Otherwise, any other OpenMM integrator can be used.
- Parameters:
temperature (float or unit.Quantity) – The temperature.
step_size (float or unit.Quantity) – The step size with which to integrate the equations of motion.
- class ufedmm.integrators.GeodesicLangevinIntegrator(temperature, friction_coefficient, step_size, num_rattles=1, scheme='LF-Middle', **kwargs)[source]¶
Bases:
AbstractMiddleRespaIntegrator
A geodesic Langevin integrator [3], which can be integrated by using either the LF-Middle or the VV-Middle scheme [2].
- Parameters:
temperature (float or unit.Quantity) – The temperature.
friction_coefficient (float or unit.Quantity) – The friction coefficient.
step_size (float or unit.Quantity) – The time-step size.
- Keyword Arguments:
num_rattles (int, default=1) – The number of RATTLE computations for geodesic integration [3]. If
num_rattles=0
, then no constraints are considered at all.scheme (str, default='LF-Middle') – Which splitting scheme will be used. Valid options are ‘VV-Middle’ and ‘LF-Middle’.
**kwargs – All other keyword arguments in
AbstractMiddleRespaIntegrator
.
Example
>>> import ufedmm >>> dt = 2*unit.femtoseconds >>> temp = 300*unit.kelvin >>> gamma = 10/unit.picoseconds >>> ufedmm.GeodesicLangevinIntegrator( ... temp, gamma, dt, num_rattles=1, scheme='VV-Middle' ... ) Per-dof variables: kT, x0 Global variables: friction = 10.0 Computation steps: 0: allow forces to update the context state 1: v <- v + 0.5*dt*f/m 2: constrain velocities 3: x <- x + 0.5*dt*v 4: x0 <- x 5: constrain positions 6: v <- v + (x - x0)/(0.5*dt) 7: constrain velocities 8: v <- z*v + sqrt((1 - z*z)*kT/m)*gaussian; z = exp(-friction*1.0*dt) 9: constrain velocities 10: x <- x + 0.5*dt*v 11: x0 <- x 12: constrain positions 13: v <- v + (x - x0)/(0.5*dt) 14: constrain velocities 15: v <- v + 0.5*dt*f/m 16: constrain velocities
- class ufedmm.integrators.MiddleMassiveGGMTIntegrator(temperature, time_constant, step_size, **kwargs)[source]¶
Bases:
AbstractMiddleRespaIntegrator
A massive, middle-type Generalized Gaussian Moment Thermostat [4] solver with optional multiple time-scale integration via RESPA.
To enable RESPA, the forces in OpenMM system must be split into distinct force groups and the keyword
respa_loop
(see below) must be a list with multiple entries.- Parameters:
temperature (float or unit.Quantity) – The temperature.
time_constant (float or unit.Quantity) – The characteristic time constant.
step_size (float or unit.Quantity) – The time-step size.
- Keyword Arguments:
**kwargs – All keyword arguments in
AbstractMiddleRespaIntegrator
, exceptnum_rattles
.
Example
>>> import ufedmm >>> temp, tau, dt = 300*unit.kelvin, 10*unit.femtoseconds, 2*unit.femtoseconds >>> integrator = ufedmm.MiddleMassiveGGMTIntegrator(temp, tau, dt) >>> print(integrator) Per-dof variables: kT, Q1, Q2, v1, v2 Computation steps: 0: allow forces to update the context state 1: v <- v + 0.5*dt*f/m 2: x <- x + 0.5*dt*v 3: v1 <- v1 + 0.5*dt*(m*v^2 - kT)/Q1 4: v2 <- v2 + 0.5*dt*((m*v^2)^2/3 - kT^2)/Q2 5: v <- v*exp(-1.0*dt*(v1 + kT*v2))/sqrt(1 + 2.0*dt*m*v^2*v2/3) 6: v1 <- v1 + 0.5*dt*(m*v^2 - kT)/Q1 7: v2 <- v2 + 0.5*dt*((m*v^2)^2/3 - kT^2)/Q2 8: x <- x + 0.5*dt*v 9: v <- v + 0.5*dt*f/m
- class ufedmm.integrators.MiddleMassiveNHCIntegrator(temperature, time_constant, step_size, nchain=2, track_energy=False, **kwargs)[source]¶
Bases:
AbstractMiddleRespaIntegrator
A massive, middle-type Nose-Hoover Chain Thermostat solver [5] with optional multiple time-scale integration via RESPA.
To enable RESPA, the forces in OpenMM system must be split into distinct force groups and the keyword
respa_loop
(see below) must be a list with multiple entries.- Parameters:
temperature (float or unit.Quantity) – The temperature.
time_constant (float or unit.Quantity) – The characteristic time constant.
step_size (float or unit.Quantity) – The time-step size.
- Keyword Arguments:
nchain (int, default=2) – The number of thermostats in each Nose-Hoover chain.
track_energy (bool, default=False) – Whether to track the thermostat energy term.
**kwargs – All keyword arguments in
AbstractMiddleRespaIntegrator
, exceptnum_rattles
.
Example
>>> import ufedmm >>> temp, tau, dt = 300*unit.kelvin, 10*unit.femtoseconds, 2*unit.femtoseconds >>> integrator = ufedmm.MiddleMassiveNHCIntegrator( ... temp, tau, dt, respa_loops=[4, 1], unroll_loops=False ... ) >>> print(integrator) Per-dof variables: kT, Q, v1, v2 Global variables: irespa0 = 0.0 Computation steps: 0: allow forces to update the context state 1: v <- v + 0.5*dt*f1/m 2: irespa0 <- 0 3: while (irespa0 < 3.5): 4: v <- v + 0.125*dt*f0/m 5: x <- x + 0.125*dt*v 6: v2 <- v2 + 0.125*dt*(Q*v1^2 - kT)/Q 7: v1 <- (v1*z + 0.125*dt*(m*v^2 - kT)/Q)*z; z=exp(-0.0625*dt*v2) 8: v <- v*exp(-0.25*dt*v1) 9: v1 <- (v1*z + 0.125*dt*(m*v^2 - kT)/Q)*z; z=exp(-0.0625*dt*v2) 10: v2 <- v2 + 0.125*dt*(Q*v1^2 - kT)/Q 11: x <- x + 0.125*dt*v 12: v <- v + 0.125*dt*f0/m 13: irespa0 <- irespa0 + 1 14: end 15: v <- v + 0.5*dt*f1/m
- class ufedmm.integrators.RegulatedNHLIntegrator(temperature, time_constant, friction_coefficient, step_size, regulation_parameter, semi_regulated=True, split_ornstein_uhlenbeck=True, **kwargs)[source]¶
Bases:
AbstractMiddleRespaIntegrator
A regulated version of the massive Nose-Hoover-Langevin [6, 7] method. Regulation means that the system Hamiltonian is modified so that velocities remain below a temperature-dependent limit. This is closely related to the SIN(R) method [8] and allows multiple time-scale integration with very large outer time steps, without resonance.
The following SDE system is solved for every degree of freedom in the system:
\[\begin{split}& dr_i = v_i dt \\ & dp_i = F_i dt - v_{\eta_i} m_i v_i dt \\ & dv_{\eta_i} = \frac{1}{Q}\left(\frac{n+1}{n} m_i v_i^2 - k_B T\right) dt - \gamma v_{\eta_i} dt + \sqrt{\frac{2\gamma k_B T}{Q}} dW_i,\end{split}\]where:
\[v_i = c_i \tanh\left(\frac{p_i}{m_i c_i}\right).\]Here, \(n\) is the regulation parameter and \(c_i\) is the maximum speed for degree of freedom i, given by \(c_i = \sqrt{\frac{n k T}{m_i}}\). The inertial parameter \(Q\) is defined as \(Q = n k_B T \tau^2\), with \(\tau\) being a relaxation time [9]. An approximate solution is obtained by applying the Trotter-Suzuki splitting formula:
\[e^{\Delta t\mathcal{L}} = e^{\frac{\Delta t}{2}\mathcal{L}^1_p} \left[e^{\frac{\delta t}{2}\mathcal{L}^0_p} e^{\frac{\delta t}{2}\mathcal{L}_r} e^{\delta t \mathcal{L}_\mathrm{bath}} e^{\frac{\delta t}{2}\mathcal{L}_r} e^{\frac{\delta t}{2}\mathcal{L}^0_p}\right]^m e^{\frac{\Delta t}{2}\mathcal{L}^1_p}\]where \(\delta t = \frac{\Delta t}{m}\). Each exponential operator above is the solution of a differential equation.
The exact solution for the physical-system part is:
\[r_i(t) = r_i^0 + c_i \mathrm{tanh}\left(\frac{p_i}{m c_i}\right) t\]\[p_i(t) = p_i^0 + F_i t\]The bath propagator is further split as:
\[e^{\delta t \mathcal{L}_\mathrm{bath}} = e^{\frac{\delta t}{2m}\mathcal{L}_B} e^{\frac{\delta t}{2m}\mathcal{L}_S} e^{\frac{\delta t}{m}\mathcal{L}_O} e^{\frac{\delta t}{2m}\mathcal{L}_S} e^{\frac{\delta t}{2m}\mathcal{L}_B}\]Part ‘B’ is a boost, whose solution is:
\[v_{\eta_i}(t) = v_{\eta_i}^0 + \frac{1}{Q}\left(\frac{n+1}{n} m_i v_i^2 - k_B T\right) t\]Part ‘S’ is a scaling, whose solution is:
\[p_i(t) = m_i c_i \mathrm{arcsinh}\left[ \sinh\left(\frac{p_i^0}{m_i c_i}\right) e^{- v_{\eta_i} t} \right]\]Part ‘O’ is an Ornstein–Uhlenbeck process, whose solution is:
\[v_{\eta_i}(t) = v_{\eta_i}^0 e^{-\gamma t} + \sqrt{\frac{k_B T}{Q}(1-e^{-2\gamma t})} R_N\]where \(R_N\) is a normally distributed random number.
- Parameters:
step_size (float or unit.Quantity) – The outer step size with which to integrate the equations of motion.
loops (int) – The number of internal substeps at each time step.
temperature (unit.Quantity) – The temperature of the heat bath.
time_scale (unit.Quantity (time)) – The relaxation time (\(\tau\)) of the Nose-Hoover thermostat.
friction_coefficient (unit.Quantity (1/time)) – The friction coefficient (\(\gamma\)) of the Langevin thermostat.
regulation_parameter (int or float) – The regulation parameter n.
- Keyword Arguments:
semi_regulated (bool, default=True) – Whether to use the semi-regulated NHL version of the method instead of its fully-regulated version.
split_ornstein_uhlenbeck (bool, default=True) – Whether to split the drifted Ornstein-Uhlenbeck operator.
**kwargs – All keyword arguments in
AbstractMiddleRespaIntegrator
, exceptnum_rattles
.
- ufedmm.integrators.add_inner_nonbonded_force(system, inner_switch, inner_cutoff, force_group_index)[source]¶
Adds a new force group to an openmm.System containing a openmm.NonbondedForce object, with the purpose of performing multiple time-scale integration via the RESPA2 splitting scheme of Morrone, Zhou, and Berne [10]. Also, assigns the provided force_group_index to this new group and force_group_index+1 to the original openmm.NonbondedForce. When used in an instance of any subclass of
AbstractMiddleRespaIntegrator
, the new force group must be set as embodied by the openmm.NonbondedForce as opposed to being complimentary to it.- Parameters:
system (openmm.System) – The system the inner force will be added to, which must contain a openmm.NonbondedForce.
inner_switch (float or unit.Quantity) – The inner switching distance, where the interaction of an atom pair begins to switch off to zero.
inner_cutoff (float or unit.Quantity) – The inner cutoff distance, where the interaction of an atom pairs completely switches off.
force_group_index (int) – The force group the new interactions will belong to. The old openmm.NonbondedForce will be automatically assigned to force_group_index+1.
Example
>>> import ufedmm >>> from openmm import unit >>> dt = 2*unit.femtoseconds >>> temp = 300*unit.kelvin >>> tau = 10*unit.femtoseconds >>> gamma = 10/unit.picoseconds >>> model = ufedmm.AlanineDipeptideModel() >>> ufedmm.add_inner_nonbonded_force( ... model.system, 5*unit.angstroms, 8*unit.angstroms, 1 ... )