Source code for qmmm_pme.integrators.integrator
#! /usr/bin/env python3
"""A module for defining the :class:`Integrator` base class.
"""
from __future__ import annotations
from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING
import numpy as np
from numpy.typing import NDArray
if TYPE_CHECKING:
from qmmm_pme import System
from qmmm_pme.plugins.plugin import IntegratorPlugin
KB = 8.31446261815324 # J / (mol * K)
[docs]class ModifiableIntegrator(ABC):
"""An abstract :class:`Integrator` base class for interfacing with
plugins.
"""
timestep: float | int
temperature: float | int
system: System
_plugins: list[str] = []
[docs] @abstractmethod
def integrate(self) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
"""Integrate forces from the :class:`System` into new positions
and velocities.
:return: The new positions and velocities of the
:class:`System`, in Angstroms and Angstroms per
femtosecond, respectively.
.. note:: Based on the implementation of the integrator
kernels from OpenMM.
"""
[docs] @abstractmethod
def compute_velocities(self) -> NDArray[np.float64]:
"""Calculate initial velocities based on the Maxwell-Boltzmann
distribution at a given temperature.
:return: The sampled velocities, in Angstroms per femtosecond.
.. note:: Based on the implementation in ASE.
"""
[docs] @abstractmethod
def compute_kinetic_energy(self) -> float:
"""Calculate the kinetic energy of the :class:`System`.
:return: The kinetic energy, in kJ/mol.
.. note:: Based on the implementation in OpenMM
"""
[docs] def register_plugin(self, plugin: IntegratorPlugin) -> None:
"""Register a :class:`Plugin` modifying an :class:`Integrator`
routine.
:param plugin: An :class:`IntegratorPlugin` object.
"""
self._plugins.append(type(plugin).__name__)
plugin.modify(self)
[docs] def active_plugins(self) -> list[str]:
"""Get the current list of active plugins.
:return: A list of the active plugins being employed by the
:class:`Integrator`.
"""
return self._plugins
[docs]@dataclass
class Integrator(ModifiableIntegrator):
"""An abstract :class:`Integrator` base class, which also contains
implementations for generating velocities and calculating kinetic
energies.
:param timestep: |timestep|
:param temperature: |temperature|
:param system: |system| to integrate.
"""
timestep: float | int
temperature: float | int
system: System
[docs] def compute_velocities(self) -> NDArray[np.float64]:
avg_ke = self.temperature * KB
masses = self.system.state.masses().reshape((-1, 1)) * (10**-3)
np.random.seed(10101)
z = np.random.standard_normal((len(masses), 3))
momenta = z * np.sqrt(avg_ke * masses)
velocities = (momenta / masses) * (10**-5)
return velocities
[docs] def compute_kinetic_energy(self) -> float:
masses = self.system.state.masses().reshape(-1, 1)
velocities = (
self.system.state.velocities()
+ (
0.5*self.timestep
* self.system.state.forces()*(10**-4)/masses
)
)
kinetic_energy = np.sum(0.5*masses*(velocities)**2) * (10**4)
return kinetic_energy