Source code for pydft_qmmm.plugins.settle.settle
"""Plugin for applying SETTLE to select residues after integration.
"""
from __future__ import annotations
from typing import Callable
from typing import TYPE_CHECKING
import numpy as np
from numpy.typing import NDArray
from .settle_utils import settle_positions
from .settle_utils import settle_velocities
from pydft_qmmm.plugins.plugin import IntegratorPlugin
if TYPE_CHECKING:
from pydft_qmmm.integrator import Integrator
from pydft_qmmm.integrator import Returns
from pydft_qmmm import System
[docs]
class SETTLE(IntegratorPlugin):
r"""Apply the SETTLE algorithm to water residues after integration.
Args:
query: The VMD-like selection query which should correspond to
water residues.
oh_distance: The distance between the oxygen and hydrogens
(:math:`\mathrm{\mathring{A}}`).
hh_distance: The distance between the hydrogens
(:math:`\mathrm{\mathring{A}}`).
"""
def __init__(
self,
query: str = "resname HOH",
oh_distance: float | int = 1.,
hh_distance: float | int = 1.632981,
) -> None:
self.query = "(" + query + ") and not subsystem I"
self.oh_distance = oh_distance
self.hh_distance = hh_distance
[docs]
def modify(
self,
integrator: Integrator,
) -> None:
"""Modify the functionality of an integrator.
Args:
integrator: The integrator whose functionality will be
modified by the plugin.
"""
self._modifieds.append(type(integrator).__name__)
self.integrator = integrator
integrator.integrate = self._modify_integrate(integrator.integrate)
integrator.compute_kinetic_energy = self._modify_compute_kinetic_energy(
integrator.compute_kinetic_energy,
)
[docs]
def constrain_velocities(self, system: System) -> NDArray[np.float64]:
"""Apply the SETTLE algorithm to system velocities.
Args:
system: The system whose velocities will be SETTLEd.
Returns:
New velocities which result from the application of the
SETTLE algorithm to system velocities.
"""
residues = self._get_hoh_residues(system)
velocities = settle_velocities(
residues,
system.positions,
system.velocities,
system.masses,
)
return velocities
def _get_hoh_residues(self, system: System) -> list[list[int]]:
"""Get the water residues from the system.
Args:
system: The system with water residues.
Returns:
A list of list of atom indices, representing the all water
residues in the system.
"""
residue_indices = sorted({
system[i].residue for i in system.select(self.query)
})
residues: list[list[int]] = [[] for _ in residue_indices]
for i, atom in enumerate(system):
if atom.residue in residue_indices:
residues[residue_indices.index(atom.residue)].append(i)
if any([len(residue) != 3 for residue in residues]):
raise ValueError("Some SETTLE residues do not have 3 atoms")
return residues
def _modify_integrate(
self,
integrate: Callable[[System], Returns],
) -> Callable[[System], Returns]:
"""Modify the integrate routine to perform SETTLE afterward.
Args:
integrate: The integration routine to modify.
Returns:
The modified integration routine which implements the SETTLE
algorithm after integration.
"""
def inner(system: System) -> Returns:
positions, velocities = integrate(system)
residues = self._get_hoh_residues(system)
positions = settle_positions(
residues,
system.positions,
positions,
system.masses,
self.oh_distance,
self.hh_distance,
)
velocities[residues, :] = (
(
positions[residues, :]
- system.positions[residues, :]
) / self.integrator.timestep
)
return positions, velocities
return inner
def _modify_compute_kinetic_energy(
self,
compute_kinetic_energy: Callable[[System], float],
) -> Callable[[System], float]:
"""Modify the kinetic energy computation to use SETTLE.
Args:
compute_kinetic_energy: The kinetic energy routine to
modify.
Returns:
The modified kinetic energy routine which applies the SETTLE
algorithm to velocities.
"""
def inner(system: System) -> float:
masses = system.masses.reshape(-1, 1)
velocities = (
system.velocities
+ (
0.5*self.integrator.timestep
* system.forces*(10**-4)/masses
)
)
residues = self._get_hoh_residues(system)
velocities = settle_velocities(
residues,
system.positions,
velocities,
system.masses,
)
kinetic_energy = (
np.sum(0.5*masses*(velocities)**2)
* (10**4)
)
return kinetic_energy
return inner