import numpy as np import openmm.unit as unit from abc import abstractmethod, ABC from scipy.constants import physical_constants class BaseDynamics(ABC): def __init__(self, args, state): super().__init__() self.start_file = f"./data/{args.molecule}/{state}.pdb" self.temperature = args.temperature * unit.kelvin self.friction = args.friction / unit.femtoseconds self.timestep = args.timestep * unit.femtoseconds self.pdb, self.integrator, self.simulation, self.external_force = self.setup() self.get_md_info() self.simulation.minimizeEnergy() self.position = self.report()[0] @abstractmethod def setup(self): pass def get_md_info(self): self.num_particles = self.simulation.system.getNumParticles() m = np.array([ self.simulation.system.getParticleMass(i).value_in_unit(unit.dalton) for i in range(self.num_particles) ]) self.heavy_atoms = m > 1.1 m = unit.Quantity(m, unit.dalton) unadjusted_variance = ( 2 * self.timestep * self.friction * unit.BOLTZMANN_CONSTANT_kB * self.temperature / m[:, None] ) std_SI_units = ( 1 / physical_constants["unified atomic mass unit"][0] * unadjusted_variance.value_in_unit(unit.joule / unit.dalton) ) self.std = unit.Quantity( np.sqrt(std_SI_units), unit.meter / unit.second ).value_in_unit(unit.nanometer / unit.femtosecond) self.m = m.value_in_unit(unit.dalton) def step(self, forces): for i in range(forces.shape[0]): self.external_force.setParticleParameters(i, i, forces[i]) self.external_force.updateParametersInContext(self.simulation.context) self.simulation.step(1) def report(self): state = self.simulation.context.getState(getPositions=True, getForces=True) positions = state.getPositions().value_in_unit(unit.nanometer) forces = state.getForces().value_in_unit( unit.dalton * unit.nanometer / unit.femtosecond ** 2 ) return positions, forces def reset(self): for i in range(len(self.position)): self.external_force.setParticleParameters(i, i, [0, 0, 0]) self.external_force.updateParametersInContext(self.simulation.context) self.simulation.context.setPositions(self.position) self.simulation.context.setVelocitiesToTemperature(self.temperature) def set_temperature(self, temperature): self.integrator.setTemperature(temperature * unit.kelvin) def energy_function(self, positions): forces, potentials = [], [] for i in range(len(positions)): self.simulation.context.setPositions(positions[i]) state = self.simulation.context.getState(getForces=True, getEnergy=True) force = state.getForces().value_in_unit( unit.dalton * unit.nanometer / unit.femtosecond ** 2 ) potential = state.getPotentialEnergy().value_in_unit( unit.kilojoules / unit.mole ) forces.append(force) potentials.append(potential) return np.array(forces), np.array(potentials)