Source code for qmmm_pme.interfaces.interface

#! /usr/bin/env python3
"""A module to define the :class:`SoftwareInterface` base class and the
various :class:`SoftwareSettings` classes.
"""
from __future__ import annotations

from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass
from enum import Enum
from typing import Callable
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from qmmm_pme import System
    from numpy.typing import NDArray
    import numpy as np


[docs]class SoftwareTypes(Enum): """Enumerations of the different types of software to interface with. """ QM = "A software for performing QM calculations." MM = "A software for performing MM calculations."
[docs]class SystemTypes(Enum): """Enumerations of the types of subsystems for a QM/MM calculation. """ SYSTEM = "A System." SUBSYSTEM = "A Subsystem of a QM/MM System." EMBEDDING = "A Subsystem for Mechanical Embedding."
[docs]class SoftwareSettings(ABC): """An abstract :class:`SoftwareSettings` base class. .. note:: This currently doesn't do anything. """
[docs]@dataclass(frozen=True) class MMSettings(SoftwareSettings): """An immutable wrapper class which holds settings for an MM software interface. :param system: |system| to perform MM calculations on. :param nonbonded_method: |nonbonded_method| :param nonbonded_cutoff: |nonbonded_cutoff| :param pme_gridnumber: |pme_gridnumber| :param pme_alpha: |pme_alpha| :param temperature: |temperature| :param friction: |friction| :param timestep: |timestep| """ system: System nonbonded_method: str = "PME" nonbonded_cutoff: float | int = 14. pme_gridnumber: int = 30 pme_alpha: float | int = 5. temperature: float | int = 300. friction: float | int = 0.001 timestep: float | int = 1.
[docs]@dataclass(frozen=True) class QMSettings(SoftwareSettings): """An immutable wrapper class which holds settings for a QM software interface. :param system: |system| to perform QM calculations on. :param basis_set: |basis_set| :param functional: |functional| :param charge: |charge| :param spin: |spin| :param quadrature_spherical: |quadrature_spherical| :param quadrature_radial: |quadrature_radial| :param scf_type: |scf_type| :param read_guess: |read_guess| :param reference_energy: |reference_energy| """ system: System basis_set: str functional: str charge: int spin: int quadrature_spherical: int = 302 quadrature_radial: int = 75 scf_type: str = "df" read_guess: bool = True reference_energy: float | int | None = None
[docs]class SoftwareInterface(ABC): """The abstract :class:`SoftwareInterface` base class. """
[docs] @abstractmethod def compute_energy(self) -> float: """Compute the energy for the :class:`System` with the :class:`SoftwareInterface`. :return: The calculated energy, in kJ/mol. """
[docs] @abstractmethod def compute_forces(self) -> NDArray[np.float64]: """Compute the forces for the :class:`System` with the :class:`SoftwareInterface`. :return: The calculated forces, in kJ/mol/Angstrom. """
[docs] @abstractmethod def compute_components(self) -> dict[str, float]: """Compute the components of the potential energy for the :class:`System` with the :class:`SoftwareInterface`. :return: The individual contributions to the energy, in kJ/mol. """
[docs] @abstractmethod def get_state_notifiers( self, ) -> dict[str, Callable[[NDArray[np.float64]], None]]: """Get the methods which should be called when a given :class:`StateVariable` is updated. :return: A dictionary of :class:`StateVariable` names and their respective notifier methods. """
[docs] @abstractmethod def get_topology_notifiers( self, ) -> dict[str, Callable[..., None]]: """Get the methods which should be called when a given :class:`TopologyVariable` is updated. :return: A dictionary of :class:`TopologyVariable` names and their respective notifier methods. """