| 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() |
|
|