def VelocityVerletIntegrator(timestep): """ Construct a velocity Verlet integrator. ARGUMENTS timestep (numpy.unit.Quantity compatible with femtoseconds) - the integration timestep RETURNS integrator (simtk.openmm.CustomIntegrator) - a velocity Verlet integrator NOTES This code is verbatim from Peter Eastman's example. """ integrator = openmm.CustomIntegrator(timestep) integrator.addPerDofVariable("x1", 0) integrator.addUpdateContextState() integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() return integrator
def test_pressure_with_bath_temperature(): system, positions, topology = readSystem('q-SPC-FW') platform = openmm.Platform.getPlatformByName('Reference') computer = atomsmm.PressureComputer(system, topology, platform, temperature=300 * unit.kelvin) context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) state = context.getState(getPositions=True, getVelocities=True, getForces=True) computer.import_configuration(state) atomic_virial = computer.get_atomic_virial() assert atomic_virial / atomic_virial.unit == pytest.approx( -11661.677650154408) atomic_pressure = computer.get_atomic_pressure() assert atomic_pressure / atomic_pressure.unit == pytest.approx( -58.64837784125407) molecular_virial = computer.get_molecular_virial(state.getForces()) assert molecular_virial / molecular_virial.unit == pytest.approx( -5418.629781093525) molecular_pressure = computer.get_molecular_pressure(state.getForces()) assert molecular_pressure / molecular_pressure.unit == pytest.approx( -554.9525554206972)
def _compute_forces(self, ufed, dataframe): collective_variables = [ colvar.id for v in ufed.variables for colvar in v.colvars ] extended_variables = [v.id for v in ufed.variables] all_variables = collective_variables + extended_variables force = openmm.CustomCVForce(_get_energy_function(ufed.variables)) for key, value in _get_parameters(ufed.variables).items(): force.addGlobalParameter(key, value) for variable in all_variables: force.addGlobalParameter(variable, 0) for xv in extended_variables: force.addEnergyParameterDerivative(xv) system = openmm.System() system.addForce(force) system.addParticle(0) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions([openmm.Vec3(0, 0, 0)]) n = len(dataframe.index) forces = [np.empty(n) for xv in extended_variables] for j, row in dataframe.iterrows(): for variable in all_variables: context.setParameter(variable, row[variable]) state = context.getState(getParameterDerivatives=True) derivatives = state.getEnergyParameterDerivatives() for i, xv in enumerate(extended_variables): forces[i][j] = -derivatives[xv] return forces
def test_inner_exceptions(): model = ufedmm.AlanineDipeptideModel() nbforce = next( filter(lambda f: isinstance(f, openmm.NonbondedForce), model.system.getForces())) rs = 0.2 rc = 0.4 ufedmm.add_inner_nonbonded_force(model.system, rs, rc, 1) model.system.getForce(model.system.getNumForces() - 1).setForceGroup(3) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(model.system, openmm.CustomIntegrator(0), platform) context.setPositions(model.positions) forces1 = _standardized( context.getState(getForces=True, groups={3}).getForces()) forces2 = [0 * f for f in forces1] ONE_4PI_EPS0 = 138.93545764438198 for index in range(nbforce.getNumExceptions()): i, j, chargeprod, sigma, epsilon = map( _standardized, nbforce.getExceptionParameters(index)) rij = _standardized(model.positions[i] - model.positions[j]) r = np.linalg.norm(rij) z = (r - rs) / (rc - rs) F = S(z) * (24 * epsilon * (2 * (sigma / r)**12 - (sigma / r)**6) / r + ONE_4PI_EPS0 * chargeprod / r**2) * rij / r forces2[i] += F forces2[j] -= F for f1, f2 in zip(forces1, forces2): for i in range(3): assert f1[i] == pytest.approx(f2[i])
def test_radius_of_gyration(): model = ufedmm.AlanineDipeptideModel() R = model.positions._value N = len(R) Rmean = sum(R, openmm.Vec3(0, 0, 0)) / N RgSqVal = 0.0 for r in R: dr = r - Rmean RgSqVal += dr.x**2 + dr.y**2 + dr.z**2 RgSqVal /= N RgSq = cvlib.SquareRadiusOfGyration(range(model.topology._numAtoms)) RgSq.setForceGroup(1) model.system.addForce(RgSq) Rg = cvlib.RadiusOfGyration(range(model.topology._numAtoms)) Rg.setForceGroup(2) model.system.addForce(Rg) integrator = openmm.CustomIntegrator(0) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(model.system, integrator, platform) context.setPositions(model.positions) RgSq = context.getState(getEnergy=True, groups={1}).getPotentialEnergy()._value assert RgSq == pytest.approx(RgSqVal) Rg = context.getState(getEnergy=True, groups={2}).getPotentialEnergy()._value assert Rg * Rg == pytest.approx(RgSqVal)
def DummyIntegrator(): """ Construct a dummy integrator that does nothing except update call the force updates. Returns ------- integrator : simtk.openmm.CustomIntegrator A dummy integrator. Examples -------- Create a dummy integrator. >>> integrator = DummyIntegrator() """ timestep = 0.0 * units.femtoseconds integrator = mm.CustomIntegrator(timestep) integrator.addUpdateContextState() integrator.addConstrainPositions() integrator.addConstrainVelocities() return integrator
def test_pressure_with_exceptions(): system, positions, topology = readSystem('emim_BCN4_Jiung2014') platform = openmm.Platform.getPlatformByName('Reference') computer = atomsmm.PressureComputer(system, topology, platform, temperature=300 * unit.kelvin) context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) state = context.getState(getPositions=True, getVelocities=True, getForces=True) computer.import_configuration(state) atomic_virial = computer.get_atomic_virial() assert atomic_virial / atomic_virial.unit == pytest.approx( -22827.477810819175) atomic_pressure = computer.get_atomic_pressure() assert atomic_pressure / atomic_pressure.unit == pytest.approx( -282.7243180164338) molecular_virial = computer.get_molecular_virial(state.getForces()) assert molecular_virial / molecular_virial.unit == pytest.approx( -23272.958585794207) molecular_pressure = computer.get_molecular_pressure(state.getForces()) assert molecular_pressure / molecular_pressure.unit == pytest.approx( -3283.563262288828)
def CustomLangevinIntegrator(temperature=298.0*u.kelvin, collision_rate=91.0/u.picoseconds, timestep=1.0*u.femtoseconds): # Compute constants. kT = kB * temperature gamma = collision_rate # Create a new custom integrator. integrator = mm.CustomIntegrator(timestep) # # If dimensions == 2, set up a dummy variable to remove z-axial velocities # if dimensions == 2: integrator.addPerDofVariable("dumv", 1.0) integrator.setPerDofVariableByName("dumv", [mm.Vec3(x=1.0, y=1.0, z=0.0)]) # # Integrator initialization. # integrator.addComputePerDof("sigma", "sqrt(kT/m)") integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addGlobalVariable("T", temperature) # temperature integrator.addGlobalVariable("b", np.exp(-gamma*timestep)) # velocity mixing parameter integrator.addPerDofVariable("sigma", 0) integrator.addPerDofVariable("x1", 0) # position before application of constraints # # Allow context updating here. # integrator.addUpdateContextState(); # # Velocity perturbation. # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") integrator.addConstrainVelocities(); # # Metropolized symplectic step. # integrator.addComputePerDof("v", "v + 0.5*dt*f/m") if dimensions == 2: # To get a 2D system, make z-velocities zero when moving x integrator.addComputePerDof("x", "x + v*dumv*dt") else: integrator.addComputePerDof("x", "x + v*dt") integrator.addComputePerDof("x1", "x") integrator.addComputePerDof("v", "v + 0.5*dt*f/m + (x-x1)/dt") # # Velocity randomization # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") if dimensions == 2: # Remove the resulting z-velocities to get the correct Kinetic Energy integrator.addComputePerDof("v", "v*dumv") return integrator
def _create_context(self, system, positions): system_copy = deepcopy(system) for force in system_copy.getForces(): force.setForceGroup(0) force_copy = deepcopy(self.force) force_copy.setForceGroup(1) system_copy.addForce(force_copy) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system_copy, openmm.CustomIntegrator(0), platform) context.setPositions(positions) return context
def potential_energy(system, positions, force_cls, scaling=None): syscopy = deepcopy(system) for force in syscopy.getForces(): if isinstance(force, force_cls): force.setForceGroup(31) integrator = openmm.CustomIntegrator(0) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(syscopy, integrator, platform) context.setPositions(positions) if scaling is not None: context.setParameter('inOutCoulombScaling', scaling) return context.getState(getEnergy=True, groups={31}).getPotentialEnergy()
def getCollectiveVariableValues(self, context): if self._context is None: integrator = openmm.CustomIntegrator(0) platform = context.getPlatform() self._context = openmm.Context(self._system, integrator, platform) self._context.setState(context.getState(getPositions=True)) energy = np.empty(self._numForces) for index in range(self._numForces): state = self._context.getState(getEnergy=True, groups=set([index])) energy[index] = state.getPotentialEnergy().value_in_unit( unit.kilojoules_per_mole) return energy
def __init__(self, system, topology, platform, properties=dict(), temperature=None): self._system = atomsmm.ComputingSystem(system) super().__init__(self._system, openmm.CustomIntegrator(0), platform, properties) self._mols = _MoleculeTotalizer(self, topology) self._kT = None if temperature is None else unit.MOLAR_GAS_CONSTANT_R * temperature self._make_obsolete()
def AndersenVelocityVerletIntegrator(timestep, friction, temperature): """ Construct a velocity Verlet integrator. ARGUMENTS timestep (numpy.unit.Quantity compatible with femtoseconds) - the integration timestep RETURNS integrator (simtk.openmm.CustomIntegrator) - a velocity Verlet integrator NOTES This code is verbatim from Peter Eastman's example. """ integrator = openmm.CustomIntegrator(timestep) # # Integrator setup. # kT = kB * temperature integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addPerDofVariable( "sigma_v", 0) # velocity distribution stddev for Maxwell-Boltzmann (set later) integrator.addPerDofVariable("x1", 0) # for constraints # # Update velocities from Maxwell-Boltzmann distribution. # integrator.addComputePerDof("sigma_v", "sqrt(kT/m)") integrator.addComputePerDof("v", "sigma_v*gaussian") # # Velocity Verlet # integrator.addUpdateContextState() integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() return integrator
def evaluate(self, system, positions): """ Computes the value of the collective variable for a given set of particle coordinates and box vectors. Whether periodic boundary conditions will be used or not depends on the corresponding attribute of the Force_ object specified as the collective variable. Parameters ---------- system : openmm.System The system. positions : list of openmm.Vec3 A list whose length equals the number of particles in the system and which contains the coordinates of these particles. Returns ------- float Example ------- >>> import ufedmm >>> from simtk import unit >>> model = ufedmm.AlanineDipeptideModel() >>> mass = 50*unit.dalton*(unit.nanometer/unit.radians)**2 >>> K = 1000*unit.kilojoules_per_mole/unit.radians**2 >>> Ts = 1500*unit.kelvin >>> bound = 180*unit.degrees >>> phi = ufedmm.CollectiveVariable('phi', model.phi, -bound, bound, mass, K, Ts) >>> psi = ufedmm.CollectiveVariable('psi', model.psi, -bound, bound, mass, K, Ts) >>> phi.evaluate(model.system, model.positions) 3.141592653589793 >>> psi.evaluate(model.system, model.positions) 3.141592653589793 """ new_system = openmm.System() new_system.setDefaultPeriodicBoxVectors( *system.getDefaultPeriodicBoxVectors()) for index in range(len(positions)): new_system.addParticle(system.getParticleMass(index)) new_system.addForce(copy.deepcopy(self.openmm_force)) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(new_system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) energy = context.getState(getEnergy=True).getPotentialEnergy() return energy.value_in_unit(unit.kilojoules_per_mole)
def setPositions(self, positions, extended_positions=None): """ Sets the positions of all particles and extended-space variables in this context. If the latter are not provided, then suitable values are automatically determined from the particle positions. Parameters ---------- positions : list of openmm.Vec3 The positions of physical particles. Keyword Args ------------ extended_positions : list of float or unit.Quantity The positions of extended-space particles. """ a, b, _ = self.getState().getPeriodicBoxVectors() nvars = len(self.variables) particle_positions = _standardized(positions) if extended_positions is None: extra_positions = [openmm.Vec3(0, b.y*(i + 1)/(nvars + 2), 0) for i in range(nvars)] minisystem = openmm.System() expression = _get_energy_function(self.variables) for i, v in enumerate(self.variables): expression += f'; {v.id}={v._get_energy_function(index=i+1)}' force = openmm.CustomCompoundBondForce(nvars, expression) force.addBond(range(nvars), []) for name, value in _get_parameters(self.variables).items(): force.addGlobalParameter(name, value) force.addGlobalParameter('Lx', a.x) for v in self.variables: minisystem.addParticle(v._particle_mass(a.x)) for cv in v.colvars: value = cv.evaluate(self.getSystem(), particle_positions + extra_positions) force.addGlobalParameter(cv.id, value) minisystem.addForce(force) minicontext = openmm.Context(minisystem, openmm.CustomIntegrator(0), openmm.Platform.getPlatformByName('Reference')) minicontext.setPositions(extra_positions) openmm.LocalEnergyMinimizer.minimize(minicontext, 1*unit.kilojoules_per_mole, 0) ministate = minicontext.getState(getPositions=True) extra_positions = ministate.getPositions().value_in_unit(unit.nanometers) else: extra_positions = [openmm.Vec3(x, b.y*(i + 1)/(nvars + 2), 0) for i, x in enumerate(extended_positions)] super().setPositions(particle_positions + extra_positions)
def VelocityVerletIntegrator(timestep=1.0 * simtk.unit.femtoseconds): """ Construct a velocity Verlet integrator. Parameters ---------- timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1*simtk.unit.femtoseconds The integration timestep. Returns ------- integrator : simtk.openmm.CustomIntegrator A velocity Verlet integrator. Notes ----- This integrator is taken verbatim from Peter Eastman's example appearing in the CustomIntegrator header file documentation. References ---------- W. C. Swope, H. C. Andersen, P. H. Berens, and K. R. Wilson, J. Chem. Phys. 76, 637 (1982) Examples -------- Create a velocity Verlet integrator. >>> timestep = 1.0 * simtk.unit.femtoseconds >>> integrator = VelocityVerletIntegrator(timestep) """ integrator = mm.CustomIntegrator(timestep) integrator.addPerDofVariable("x1", 0) integrator.addUpdateContextState() integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() return integrator
def test_AlchemicalRespaSystem_with_softcore(): system, positions, topology, solute = readSystem('phenol-in-water') respa_info = dict(rcutIn=7 * unit.angstroms, rswitchIn=5 * unit.angstroms) solvation_system = atomsmm.systems.AlchemicalRespaSystem( system, *respa_info.values(), solute, use_softcore=True, ) state = {'lambda': 0.5, 'respa_switch': 1} components = atomsmm.splitPotentialEnergy(solvation_system, topology, positions, **state) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) force = solvation_system.get_alchemical_vdw_force( [i / 5 for i in range(6)]) values = force.getCollectiveVariableValues( context) * unit.kilojoules_per_mole for index in range(force.getNumCollectiveVariables()): name = force.getCollectiveVariableName(index) components[name] = values[index] for item in components.items(): print(*item) potential = {} potential['HarmonicBondForce'] = 2621.3223922886677 # kJ/mol potential['HarmonicAngleForce'] = 1525.1006876561419 # kJ/mol potential['PeriodicTorsionForce'] = 18.767576693568476 # kJ/mol potential['Real-Space'] = 80089.51116719692 # kJ/mol potential['Reciprocal-Space'] = -107038.52551657759 # kJ/mol potential['CustomNonbondedForce'] = 5037.152491649265 # kJ/mol potential['CustomBondForce'] = -53.526446723139806 # kJ/mol potential['CustomBondForce(1)'] = -53.374675325650806 # kJ/mol potential['CustomNonbondedForce(1)'] = -24.140118811594814 # kJ/mol potential['CustomNonbondedForce(2)'] = -24.140118811594814 # kJ/mol potential['Total'] = -17901.852560765 # kJ/mol potential['E0'] = 0.0 # kJ/mol potential['E1'] = -10.071581499620784 # kJ/mol potential['E2'] = -19.66450283710424 # kJ/mol potential['E3'] = -28.284595753200428 # kJ/mol potential['E4'] = -35.004158250494505 # kJ/mol potential['E5'] = -37.9416812137183 # kJ/mol for term, value in components.items(): assert value / value.unit == pytest.approx(potential[term])
def evaluate(self, positions, boxVectors=None): """ Computes the value of the collective variable for a given set of particle coordinates and box vectors. Whether periodic boundary conditions will be used or not depends on the corresponding attribute of the Force_ object specified as the collective variable. Parameters ---------- positions : list(openmm.Vec3) A list whose length equals the number of particles in the system and which contains the coordinates of these particles. boxVectors : list(openmm.Vec3), optional, default=None A list with three vectors which describe the edges of the simulation box. Returns ------- unit.Quantity Example ------- >>> import afed >>> from simtk import unit >>> model = afed.AlanineDipeptideModel() >>> psi_angle, _ = model.getDihedralAngles() >>> psi = afed.DrivenCollectiveVariable('psi', psi_angle, unit.radians, period=360*unit.degrees) >>> psi.evaluate(model.getPositions()) Quantity(value=3.141592653589793, unit=radian) """ system = openmm.System() for i in range(len(positions)): system.addParticle(0) if boxVectors is not None: system.setDefaultPeriodicBoxVectors(*boxVectors) system.addForce(copy.deepcopy(self._variable)) platform = openmm.Platform.getPlatformByName('Reference') context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) energy = context.getState(getEnergy=True).getPotentialEnergy() return energy.value_in_unit(unit.kilojoules_per_mole) * self._dimension
def test_AlchemicalRespaSystem_with_coulomb_scaling(): system, positions, topology, solute = readSystem('phenol-in-water') respa_info = dict(rcutIn=7 * unit.angstroms, rswitchIn=5 * unit.angstroms) solvation_system = atomsmm.systems.AlchemicalRespaSystem( system, *respa_info.values(), solute, coupling_function='lambda^4*(5-4*lambda)', coulomb_scaling=True, lambda_coul=0.5, # middle_scale=True, ) state = {'lambda': 0.5, 'respa_switch': 1} components = atomsmm.splitPotentialEnergy(solvation_system, topology, positions, **state) integrator = openmm.CustomIntegrator(0) platform = openmm.Platform.getPlatformByName('Reference') simulation = openmm.app.Simulation(topology, solvation_system, integrator, platform) simulation.context.setPositions(positions) force = solvation_system.get_alchemical_coul_force() Ecoul = force.getCollectiveVariableValues(simulation.context) components['Ecoul'] = Ecoul[0] * unit.kilojoule_per_mole for item in components.items(): print(*item) potential = {} potential['HarmonicBondForce'] = 2621.3223922886677 # kJ/mol potential['HarmonicAngleForce'] = 1525.1006876561419 # kJ/mol potential['PeriodicTorsionForce'] = 18.767576693568476 # kJ/mol potential['Real-Space'] = 80078.91697014398 # kJ/mol potential['Reciprocal-Space'] = -107074.49398119976 # kJ/mol potential['CustomNonbondedForce'] = 5037.152491649265 # kJ/mol potential['CustomBondForce'] = -53.526446723139806 # kJ/mol potential['CustomBondForce(1)'] = -53.374675325650806 # kJ/mol potential['CustomNonbondedForce(1)'] = -23.447733015522058 # kJ/mol potential['CustomCVForce'] = -7.114065227572182 # kJ/mol potential['CustomCVForce(1)'] = -6.301336948673654 # kJ/mol potential['Total'] = -17936.998120008684 # kJ/mol potential['Ecoul'] = -93.14060537915793 # kJ/mol for term, value in components.items(): assert value / value.unit == pytest.approx(potential[term])
def perform_test(sigma, epsilon, charge0, charge1, rs, rc): nonbonded = openmm.NonbondedForce() nonbonded.addParticle(charge0, sigma, epsilon) nonbonded.addParticle(charge1, sigma, epsilon) nonbonded.setNonbondedMethod(nonbonded.CutoffNonPeriodic) platform = openmm.Platform.getPlatformByName('Reference') system = openmm.System() system.addParticle(1) system.addParticle(1) system.addForce(nonbonded) ufedmm.add_inner_nonbonded_force(system, rs, rc, 1) context = openmm.Context(system, openmm.CustomIntegrator(0), platform) ONE_4PI_EPS0 = 138.93545764438198 for r in np.linspace(sigma, rc, 101): context.setPositions([[0, 0, 0], [r, 0, 0]]) state = context.getState(getForces=True, groups={1}) force = state.getForces()[1].x F = 24 * epsilon * ( 2 * (sigma / r)**12 - (sigma / r)**6) / r + ONE_4PI_EPS0 * charge0 * charge1 / r**2 assert force == pytest.approx(F * S((r - rs) / (rc - rs)))
def test_pressure_with_kinetic_temperature(): system, positions, topology = readSystem('q-SPC-FW') platform = openmm.Platform.getPlatformByName('Reference') computer = atomsmm.PressureComputer(system, topology, platform) context = openmm.Context(system, openmm.CustomIntegrator(0), platform) context.setPositions(positions) context.setVelocitiesToTemperature(300 * unit.kelvin, 1234) state = context.getState(getPositions=True, getVelocities=True, getForces=True) computer.import_configuration(state) atomic_virial = computer.get_atomic_virial() assert atomic_virial / atomic_virial.unit == pytest.approx( -11661.677650154408) atomic_pressure = computer.get_atomic_pressure() assert atomic_pressure / atomic_pressure.unit == pytest.approx( -86.95921953101447) molecular_virial = computer.get_molecular_virial(state.getForces()) assert molecular_virial / molecular_virial.unit == pytest.approx( -5418.629781093525) molecular_pressure = computer.get_molecular_pressure(state.getForces()) assert molecular_pressure / molecular_pressure.unit == pytest.approx( -539.0081647715243)
def __init__(self, variables, variances, centers, weights, platform, properties): num_particles = len(variables) // 3 + 1 coordinates = [ f'{x}{i+1}' for i in range(num_particles) for x in 'xyz' ] exponents = [] for v, variance, x in zip(variables, variances, coordinates): if v.periodic: # von Mises factor = 2 * np.pi / v._range exponents.append( f'{1.0/(variance*factor**2)}*(cos({factor}*({v.id}-{x}))-1)' ) else: # Gauss exponents.append(f'(-{0.5/variance})*({v.id}-{x})^2') expression = f'weight*exp({"+".join(exponents)})' force = openmm.CustomCompoundBondForce(num_particles, expression) force.addPerBondParameter('weight') for v in variables: force.addGlobalParameter(v.id, 0) force.addEnergyParameterDerivative(v.id) system = openmm.System() positions = [] for i, (center, weight) in enumerate(zip(centers, weights)): for position in np.resize(center, (num_particles, 3)): system.addParticle(1.0) positions.append(openmm.Vec3(*position)) force.addBond(range(i * num_particles, (i + 1) * num_particles), [weight]) system.addForce(force) integrator = openmm.CustomIntegrator(0) super().__init__(system, integrator, platform, properties) self.parameters = [v.id for v in variables] self.setPositions(positions)
def GradientDescentMinimizationIntegrator(initial_step_size=0.01 * units.angstroms): """ Construct a simple gradient descent minimization integrator. Parameters ---------- initial_step_size : numpy.unit.Quantity compatible with nanometers, default: 0.01*simtk.unit.angstroms The norm of the initial step size guess. Returns ------- integrator : simtk.openmm.CustomIntegrator A velocity Verlet integrator. Notes ----- An adaptive step size is used. References ---------- Examples -------- Create a gradient descent minimization integrator. >>> integrator = GradientDescentMinimizationIntegrator() """ timestep = 1.0 * units.femtoseconds integrator = mm.CustomIntegrator(timestep) integrator.addGlobalVariable("step_size", initial_step_size / units.nanometers) integrator.addGlobalVariable("energy_old", 0) integrator.addGlobalVariable("energy_new", 0) integrator.addGlobalVariable("delta_energy", 0) integrator.addGlobalVariable("accept", 0) integrator.addGlobalVariable("fnorm2", 0) integrator.addPerDofVariable("x_old", 0) # Update context state. integrator.addUpdateContextState() # Constrain positions. integrator.addConstrainPositions() # Store old energy and positions. integrator.addComputeGlobal("energy_old", "energy") integrator.addComputePerDof("x_old", "x") # Compute sum of squared norm. integrator.addComputeSum("fnorm2", "f^2") # Take step. integrator.addComputePerDof("x", "x+step_size*f/sqrt(fnorm2 + delta(fnorm2))") integrator.addConstrainPositions() # Ensure we only keep steps that go downhill in energy. integrator.addComputeGlobal("energy_new", "energy") integrator.addComputeGlobal("delta_energy", "energy_new-energy_old") # Accept also checks for NaN integrator.addComputeGlobal( "accept", "step(-delta_energy) * delta(energy - energy_new)") integrator.addComputePerDof("x", "accept*x + (1-accept)*x_old") # Update step size. integrator.addComputeGlobal("step_size", "step_size * (2.0*accept + 0.5*(1-accept))") return integrator
def VVVRIntegrator(temperature=298.0 * simtk.unit.kelvin, collision_rate=91.0 / simtk.unit.picoseconds, timestep=1.0 * simtk.unit.femtoseconds): """ Create a velocity verlet with velocity randomization (VVVR) integrator. Parameters ---------- temperature : numpy.unit.Quantity compatible with kelvin, default: 298.0*simtk.unit.kelvin The temperature. collision_rate : numpy.unit.Quantity compatible with 1/picoseconds, default: 91.0/simtk.unit.picoseconds The collision rate. timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1.0*simtk.unit.femtoseconds The integration timestep. Returns ------- integrator : simtk.openmm.CustomIntegrator VVVR integrator. Notes ----- This integrator is equivalent to a Langevin integrator in the velocity Verlet discretization with a timestep correction to ensure that the field-free diffusion constant is timestep invariant. The global 'pseudowork' keeps track of the pseudowork accumulated during integration, and can be used to correct the sampled statistics or in a Metropolization scheme. TODO ---- Move initialization of 'sigma' to setting the per-particle variables. We can ditch pseudowork and instead use total energy difference - heat. References ---------- David A. Sivak, John D. Chodera, and Gavin E. Crooks. Time step rescaling recovers continuous-time dynamical properties for discrete-time Langevin integration of nonequilibrium systems http://arxiv.org/abs/1301.3800 Examples -------- Create a VVVR integrator. >>> temperature = 298.0 * simtk.unit.kelvin >>> collision_rate = 91.0 / simtk.unit.picoseconds >>> timestep = 1.0 * simtk.unit.femtoseconds >>> integrator = VVVRIntegrator(temperature, collision_rate, timestep) """ # Compute constants. kT = kB * temperature gamma = collision_rate # Create a new custom integrator. integrator = mm.CustomIntegrator(timestep) # # Integrator initialization. # integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addGlobalVariable("b", numpy.exp( -gamma * timestep)) # velocity mixing parameter integrator.addPerDofVariable("sigma", 0) integrator.addPerDofVariable( "x1", 0) # position before application of constraints # # Allow context updating here. # integrator.addUpdateContextState() # # Pre-computation. # This only needs to be done once, but it needs to be done for each degree of freedom. # Could move this to initialization? # integrator.addComputePerDof("sigma", "sqrt(kT/m)") # # Velocity perturbation. # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") integrator.addConstrainVelocities() # # Metropolized symplectic step. # integrator.addComputePerDof("v", "v + 0.5*dt*f/m") integrator.addComputePerDof("x", "x + v*dt") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v + 0.5*dt*f/m + (x-x1)/dt") integrator.addConstrainVelocities() # # Velocity randomization # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") integrator.addConstrainVelocities() return integrator
def GHMCIntegrator(temperature=298.0 * simtk.unit.kelvin, collision_rate=91.0 / simtk.unit.picoseconds, timestep=1.0 * simtk.unit.femtoseconds): """ Create a generalized hybrid Monte Carlo (GHMC) integrator. Parameters ---------- temperature : numpy.unit.Quantity compatible with kelvin, default: 298*simtk.unit.kelvin The temperature. collision_rate : numpy.unit.Quantity compatible with 1/picoseconds, default: 91.0/simtk.unit.picoseconds The collision rate. timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1.0*simtk.unit.femtoseconds The integration timestep. Returns ------- integrator : simtk.openmm.CustomIntegrator A GHMC integrator. Notes ----- This integrator is equivalent to a Langevin integrator in the velocity Verlet discretization with a Metrpolization step to ensure sampling from the appropriate distribution. Additional global variables 'ntrials' and 'naccept' keep track of how many trials have been attempted and accepted, respectively. TODO ---- Move initialization of 'sigma' to setting the per-particle variables. Examples -------- Create a GHMC integrator. >>> temperature = 298.0 * simtk.unit.kelvin >>> collision_rate = 91.0 / simtk.unit.picoseconds >>> timestep = 1.0 * simtk.unit.femtoseconds >>> integrator = GHMCIntegrator(temperature, collision_rate, timestep) References ---------- Lelievre T, Stoltz G, and Rousset M. Free Energy Computations: A Mathematical Perspective http://www.amazon.com/Free-Energy-Computations-Mathematical-Perspective/dp/1848162472 """ # Initialize constants. kT = kB * temperature gamma = collision_rate # Create a new custom integrator. integrator = mm.CustomIntegrator(timestep) # # Integrator initialization. # b = numpy.exp(-gamma * timestep) integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addGlobalVariable("b", b) # velocity mixing parameter integrator.addPerDofVariable("sigma", 0) integrator.addGlobalVariable("ke", 0) # kinetic energy integrator.addPerDofVariable("vold", 0) # old velocities integrator.addPerDofVariable("xold", 0) # old positions integrator.addGlobalVariable("Eold", 0) # old energy integrator.addGlobalVariable("Enew", 0) # new energy integrator.addGlobalVariable("accept", 0) # accept or reject integrator.addGlobalVariable("naccept", 0) # number accepted integrator.addGlobalVariable("ntrials", 0) # number of Metropolization trials integrator.addPerDofVariable( "x1", 0) # position before application of constraints integrator.addGlobalVariable("alpha", numpy.sqrt(b)) integrator.addGlobalVariable("beta", numpy.sqrt(1 - b)) integrator.addPerDofVariable("zeta", 0) # # Pre-computation. # This only needs to be done once, but it needs to be done for each degree of freedom. # Could move this to initialization? # integrator.addComputePerDof("sigma", "sqrt(kT/m)") # # Allow context updating here. # integrator.addUpdateContextState() # # Constrain positions. # integrator.addConstrainPositions() # # Velocity perturbation. # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") #integrator.addComputePerDof("v", "sqrt(b)*v") #integrator.addComputePerDof("v", "v + sqrt(1-b)*sigma*gaussian") #integrator.addComputePerDof("v", "alpha*v + beta*sigma*gaussian") #integrator.addComputePerDof("v", "alpha*v") #integrator.addComputePerDof("v", "v + beta*sigma*gaussian") #integrator.addComputePerDof("zeta", "gaussian") #integrator.addComputePerDof("v", "alpha*v + beta*sigma*zeta") integrator.addConstrainVelocities() # # Metropolized symplectic step. # integrator.addComputeSum("ke", "0.5*m*v*v") integrator.addComputeGlobal("Eold", "ke + energy") integrator.addComputePerDof("xold", "x") integrator.addComputePerDof("vold", "v") integrator.addComputePerDof("v", "v + 0.5*dt*f/m") integrator.addComputePerDof("x", "x + v*dt") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v + 0.5*dt*f/m + (x-x1)/dt") integrator.addConstrainVelocities() integrator.addComputeSum("ke", "0.5*m*v*v") integrator.addComputeGlobal("Enew", "ke + energy") integrator.addComputeGlobal("accept", "step(exp(-(Enew-Eold)/kT) - uniform)") integrator.addComputePerDof("x", "x*accept + xold*(1-accept)") integrator.addComputePerDof("v", "v*accept - vold*(1-accept)") # # Velocity randomization # integrator.addComputePerDof("v", "sqrt(b)*v + sqrt(1-b)*sigma*gaussian") #integrator.addComputePerDof("zeta", "gaussian") #integrator.addComputePerDof("v", "alpha*v + beta*sigma*zeta") integrator.addConstrainVelocities() # # Accumulate statistics. # integrator.addComputeGlobal("naccept", "naccept + accept") integrator.addComputeGlobal("ntrials", "ntrials + 1") return integrator
def HMCIntegrator(temperature=298.0 * simtk.unit.kelvin, nsteps=10, timestep=1 * simtk.unit.femtoseconds): """ Create a hybrid Monte Carlo (HMC) integrator. Parameters ---------- temperature : numpy.unit.Quantity compatible with kelvin, default: 298*simtk.unit.kelvin The temperature. nsteps : int, default: 10 The number of velocity Verlet steps to take per HMC trial. timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1*simtk.unit.femtoseconds The integration timestep. Returns ------- integrator : simtk.openmm.CustomIntegrator A hybrid Monte Carlo integrator. Warning ------- Because 'nsteps' sets the number of steps taken, a call to integrator.step(1) actually takes 'nsteps' steps. Notes ----- The velocity is drawn from a Maxwell-Boltzmann distribution, then 'nsteps' steps are taken, and the new configuration is either accepted or rejected. Additional global variables 'ntrials' and 'naccept' keep track of how many trials have been attempted and accepted, respectively. TODO ---- Currently, the simulation timestep is only advanced by 'timestep' each step, rather than timestep*nsteps. Fix this. Examples -------- Create an HMC integrator. >>> timestep = 1.0 * simtk.unit.femtoseconds # fictitious timestep >>> temperature = 298.0 * simtk.unit.kelvin >>> nsteps = 10 # number of steps per call >>> integrator = HMCIntegrator(temperature, nsteps, timestep) """ # Create a new custom integrator. integrator = mm.CustomIntegrator(timestep) # Compute the thermal energy. kT = kB * temperature # # Integrator initialization. # integrator.addGlobalVariable("naccept", 0) # number accepted integrator.addGlobalVariable("ntrials", 0) # number of Metropolization trials integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addPerDofVariable("sigma", 0) integrator.addGlobalVariable("ke", 0) # kinetic energy integrator.addPerDofVariable("xold", 0) # old positions integrator.addGlobalVariable("Eold", 0) # old energy integrator.addGlobalVariable("Enew", 0) # new energy integrator.addGlobalVariable("accept", 0) # accept or reject integrator.addPerDofVariable("x1", 0) # for constraints # # Pre-computation. # This only needs to be done once, but it needs to be done for each degree of freedom. # Could move this to initialization? # integrator.addComputePerDof("sigma", "sqrt(kT/m)") # # Allow Context updating here, outside of inner loop only. # integrator.addUpdateContextState() # # Draw new velocity. # integrator.addComputePerDof("v", "sigma*gaussian") integrator.addConstrainVelocities() # # Store old position and energy. # integrator.addComputeSum("ke", "0.5*m*v*v") integrator.addComputeGlobal("Eold", "ke + energy") integrator.addComputePerDof("xold", "x") # # Inner symplectic steps using velocity Verlet. # for step in range(nsteps): integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() # # Accept/reject step. # integrator.addComputeSum("ke", "0.5*m*v*v") integrator.addComputeGlobal("Enew", "ke + energy") integrator.addComputeGlobal("accept", "step(exp(-(Enew-Eold)/kT) - uniform)") integrator.addComputePerDof("x", "x*accept + xold*(1-accept)") # # Accumulate statistics. # integrator.addComputeGlobal("naccept", "naccept + accept") integrator.addComputeGlobal("ntrials", "ntrials + 1") return integrator
def MetropolisMonteCarloIntegrator(temperature=298.0 * simtk.unit.kelvin, sigma=0.1 * simtk.unit.angstroms, timestep=1 * simtk.unit.femtoseconds): """ Create a simple Metropolis Monte Carlo integrator that uses Gaussian displacement trials. Parameters ---------- temperature : numpy.unit.Quantity compatible with kelvin, default: 298*simtk.unit.kelvin The temperature. sigma : numpy.unit.Quantity compatible with nanometers, default: 0.1*simtk.unit.angstroms The displacement standard deviation for each degree of freedom. timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1.0*simtk.unit.femtoseconds The integration timestep, which is purely fictitious---it is just used to advance the simulation clock. Returns ------- integrator : simtk.openmm.CustomIntegrator A Metropolis Monte Carlo integrator. Warning ------- This integrator does not respect constraints. Notes ----- The timestep is purely fictitious, and just used to advance the simulation clock. Velocities are drawn from a Maxwell-Boltzmann distribution each timestep to generate correct (x,v) statistics. Additional global variables 'ntrials' and 'naccept' keep track of how many trials have been attempted and accepted, respectively. Examples -------- Create a Metropolis Monte Carlo integrator with specified random displacement standard deviation. >>> timestep = 1.0 * simtk.unit.femtoseconds # fictitious timestep >>> temperature = 298.0 * simtk.unit.kelvin >>> sigma = 1.0 * simtk.unit.angstroms >>> integrator = MetropolisMonteCarloIntegrator(temperature, sigma, timestep) """ # Create a new Custom integrator. integrator = mm.CustomIntegrator(timestep) # Compute the thermal energy. kT = kB * temperature # # Integrator initialization. # integrator.addGlobalVariable("naccept", 0) # number accepted integrator.addGlobalVariable("ntrials", 0) # number of Metropolization trials integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addPerDofVariable("sigma_x", sigma) # perturbation size integrator.addPerDofVariable( "sigma_v", 0) # velocity distribution stddev for Maxwell-Boltzmann (set later) integrator.addPerDofVariable("xold", 0) # old positions integrator.addGlobalVariable("Eold", 0) # old energy integrator.addGlobalVariable("Enew", 0) # new energy integrator.addGlobalVariable("accept", 0) # accept or reject # # Context state update. # integrator.addUpdateContextState() # # Update velocities from Maxwell-Boltzmann distribution. # integrator.addComputePerDof("sigma_v", "sqrt(kT/m)") integrator.addComputePerDof("v", "sigma_v*gaussian") integrator.addConstrainVelocities() # # propagation steps # # Store old positions and energy. integrator.addComputePerDof("xold", "x") integrator.addComputeGlobal("Eold", "energy") # Gaussian particle displacements. integrator.addComputePerDof("x", "x + sigma_x*gaussian") # Accept or reject with Metropolis criteria. integrator.addComputeGlobal("accept", "step(exp(-(energy-Eold)/kT) - uniform)") integrator.addComputePerDof("x", "(1-accept)*xold + x*accept") # Accumulate acceptance statistics. integrator.addComputeGlobal("naccept", "naccept + accept") integrator.addComputeGlobal("ntrials", "ntrials + 1") return integrator
def AndersenVelocityVerletIntegrator(temperature=298 * simtk.unit.kelvin, collision_rate=91.0 / simtk.unit.picoseconds, timestep=1.0 * simtk.unit.femtoseconds): """ Construct a velocity Verlet integrator with Andersen thermostat, implemented as per-particle collisions (rather than massive collisions). Parameters ---------- temperature : numpy.unit.Quantity compatible with kelvin, default: 298*simtk.unit.kelvin The temperature of the fictitious bath. collision_rate : numpy.unit.Quantity compatible with 1/picoseconds, default: 91/simtk.unit.picoseconds The collision rate with fictitious bath particles. timestep : numpy.unit.Quantity compatible with femtoseconds, default: 1*simtk.unit.femtoseconds The integration timestep. Returns ------- integrator : simtk.openmm.CustomIntegrator A velocity Verlet integrator with periodic Andersen thermostat. References ---------- Hans C. Andersen "Molecular dynamics simulations at constant pressure and/or temperature", Journal of Chemical Physics 72, 2384-2393 (1980) http://dx.doi.org/10.1063/1.439486 Examples -------- Create a velocity Verlet integrator with Andersen thermostat. >>> timestep = 1.0 * simtk.unit.femtoseconds >>> collision_rate = 91.0 / simtk.unit.picoseconds >>> temperature = 298.0 * simtk.unit.kelvin >>> integrator = AndersenVelocityVerletIntegrator(temperature, collision_rate, timestep) Notes ------ The velocity Verlet integrator is taken verbatim from Peter Eastman's example in the CustomIntegrator header file documentation. The efficiency could be improved by avoiding recomputation of sigma_v every timestep. """ integrator = mm.CustomIntegrator(timestep) # # Integrator initialization. # kT = kB * temperature integrator.addGlobalVariable("kT", kT) # thermal energy integrator.addGlobalVariable( "p_collision", timestep * collision_rate) # per-particle collision probability per timestep integrator.addPerDofVariable( "sigma_v", 0 ) # velocity distribution stddev for Maxwell-Boltzmann (computed later) integrator.addPerDofVariable( "collision", 0) # 1 if collision has occured this timestep, 0 otherwise integrator.addPerDofVariable("x1", 0) # for constraints # # Update velocities from Maxwell-Boltzmann distribution for particles that collide. # integrator.addComputePerDof("sigma_v", "sqrt(kT/m)") integrator.addComputePerDof( "collision", "step(p_collision-uniform)" ) # if collision has occured this timestep, 0 otherwise integrator.addComputePerDof( "v", "(1-collision)*v + collision*sigma_v*gaussian" ) # randomize velocities of particles that have collided # # Velocity Verlet step # integrator.addUpdateContextState() integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() return integrator
from simtk import unit as u import simtk.openmm as mm import openmmtools testsystem = openmmtools.testsystems.WaterBox() system, positions = testsystem.system, testsystem.positions integrator = mm.CustomIntegrator(1.75 * u.femtoseconds) temperature = 300 * u.kelvin integrator.addPerDofVariable("x1", 0) integrator.addPerDofVariable("x2", 0) integrator.addPerDofVariable("x3", 0) integrator.addGlobalVariable("e1", 0) integrator.addGlobalVariable("e2", 0) integrator.addGlobalVariable("e3", 0) integrator.addGlobalVariable("e4", 0) integrator.addGlobalVariable("e5", 0) integrator.addGlobalVariable("one", 1.0) integrator.addGlobalVariable("e2m", 0.0) integrator.addUpdateContextState() integrator.addComputePerDof("v", "v+0.5*dt*f/m") integrator.addComputePerDof("x", "x+dt*v") integrator.addComputePerDof("x1", "x") integrator.addConstrainPositions() integrator.addComputePerDof("v", "v+0.5*dt*f/m+(x-x1)/dt") integrator.addConstrainVelocities() integrator.addComputePerDof("x2", "x")
def PrecisionTestIntegrator(dx): integrator = openmm.CustomIntegrator(1.0) integrator.addPerDofVariable('dx', dx) integrator.addComputePerDof('x', 'x + dx') return integrator