|
|
from abc import abstractmethod |
|
|
|
|
|
from potentials.base import Potential |
|
|
from potentials.md_utils import ForceReporter |
|
|
|
|
|
|
|
|
class MoleculePotential(Potential): |
|
|
""" |
|
|
This is a baseclass for implementing molecular potentials using some framework. |
|
|
""" |
|
|
|
|
|
def __init__(self, start_file, index, save_file=None): |
|
|
super().__init__() |
|
|
self.start_file = start_file |
|
|
self.index = index |
|
|
self.save_file = save_file |
|
|
|
|
|
self.pdb, self.simulation, self.external_force = self.setup() |
|
|
|
|
|
self.reporter = ForceReporter(1) |
|
|
self.simulation.reporters.append(self.reporter) |
|
|
|
|
|
if self.index == 0: |
|
|
self.simulation.step(1) |
|
|
self.simulation.minimizeEnergy() |
|
|
self.simulation.saveCheckpoint(self.get_position_file()) |
|
|
|
|
|
if self.index > -1: |
|
|
self.reset() |
|
|
|
|
|
@abstractmethod |
|
|
def setup(self): |
|
|
""" |
|
|
Initializes all Framework specific configurations |
|
|
:return pdb: Description of the atom locations |
|
|
:return simulation: Simulation environment that runs the MD |
|
|
:return external_force: Reference to custom external force implementation to be updated by control |
|
|
""" |
|
|
pass |
|
|
|
|
|
@abstractmethod |
|
|
def get_position_file(self): |
|
|
""" |
|
|
Abstact method to point towards location of intermediate files. |
|
|
:return path: Path to intermediate files |
|
|
""" |
|
|
pass |
|
|
|
|
|
def drift(self, forces): |
|
|
""" |
|
|
This is the main workhorse. This function calculates the entire dynamics update. This includes the passive |
|
|
dynamics as well as the controlled dynamics. This is done by offloading the computation step entirely to |
|
|
OpenMM through the use of a "Custom External Force". |
|
|
:param forces: Control force given by the policy |
|
|
:return: New positions and velocity of the system. |
|
|
""" |
|
|
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) |
|
|
|
|
|
new_pos = self.reporter.latest_positions.copy() |
|
|
new_forces = self.reporter.latest_forces.copy() |
|
|
|
|
|
return new_pos, new_forces |
|
|
|
|
|
def reset(self): |
|
|
""" |
|
|
Resets the MD simulation to the initial minimum energy conformation. |
|
|
""" |
|
|
self.simulation.loadCheckpoint(self.get_position_file()) |
|
|
for i in range(len(self.pdb.positions)): |
|
|
self.external_force.setParticleParameters(i, i, [0, 0, 0]) |
|
|
self.external_force.updateParametersInContext(self.simulation.context) |
|
|
|
|
|
self.simulation.step(1) |
|
|
self.simulation.minimizeEnergy() |
|
|
|
|
|
def latest_position(self): |
|
|
""" |
|
|
Returns the positions after the latest update step. |
|
|
:return: Current coordinates of atoms |
|
|
""" |
|
|
return self.simulation.state.getPositions() |
|
|
|