Source code for pydft_qmmm.interfaces.interface

"""Base classes for software interfaces and settings.
"""
from __future__ import annotations

from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING
from typing import TypeVar

from pydft_qmmm.common import TheoryLevel

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

T = TypeVar("T")


[docs] class SoftwareSettings(ABC): """The abstract software settings base class. .. note:: This currently doesn't do anything. """
[docs] @dataclass(frozen=True) class MMSettings(SoftwareSettings): r"""Immutable container which holds settings for an MM interface. Args: system: The system which will be tied to the MM interface. forcefield: The files containing forcefield and topology data for the system. nonbonded_method: The method for treating non-bonded interactions, as in OpenMM. nonbonded_cutoff: The distance at which to truncate close-range non-bonded interactions. pme_gridnumber: The number of grid points to include along each lattice edge in PME summation. pme_alpha: The Gaussian width parameter in Ewald summation (:math:`\mathrm{nm^{-1}}`). """ system: System forcefield: list[str] nonbonded_method: str = "PME" nonbonded_cutoff: float | int = 14. pme_gridnumber: tuple[int, int, int] | None = None pme_alpha: float | int | None = None
[docs] @dataclass(frozen=True) class QMSettings(SoftwareSettings): """Immutable container which holds settings for an QM interface. Args: system: The system which will be tied to the QM interface. basis_set: The name of the basis set to be used in QM calculations. functional: The name of the functional set to be used in QM calculations. charge: The net charge (:math:`e`) of the system represented by the QM Hamiltonian. spin: The net spin of the system represented by the QM Hamiltonian quadrature_spherical: The number of spherical Lebedev points to use in the DFT quadrature. quadrature_radial: The number of radial points to use in the DFT quadrature. scf_type: The name of the type of SCF to perform, relating to the JK build algorithms as in Psi4. read_guess: Whether or not to reuse previous wavefunctions as initial guesses in subsequent QM calculations. """ 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
[docs] class SoftwareInterface(ABC): """The abstract software interface base class. Attributes: theory_level: The level of theory that the software applies in energy and force calculations. """ theory_level: TheoryLevel
[docs] @abstractmethod def compute_energy(self) -> float: r"""Compute the energy of the system using the external software. Returns: The energy (:math:`\mathrm{kJ\;mol^{-1}}`) of the system. """
[docs] @abstractmethod def compute_forces(self) -> NDArray[np.float64]: r"""Compute the forces on the system using the external software. Returns: The forces (:math:`\mathrm{kJ\;mol^{-1}\;\mathring{A}^{-1}}`) acting on atoms in the system. """
[docs] @abstractmethod def compute_components(self) -> dict[str, float]: r"""Compute the components of energy using the external software. Returns: The components of the energy (:math:`\mathrm{kJ\;mol^{-1}}`) of the system. """
[docs] @abstractmethod def update_threads(self, threads: int) -> None: """Set the number of threads used by the external software. Args: threads: The number of threads to utilize. """
[docs] @abstractmethod def update_memory(self, memory: str) -> None: """Set the amount of memory used by the external software. Args: memory: The amount of memory to utilize. """
[docs] class MMInterface(SoftwareInterface): """The abstract MM interface base class. Attributes: theory_level: The level of theory that the software applies in energy and force calculations. """ theory_level = TheoryLevel.MM
[docs] @abstractmethod def zero_intramolecular(self, atoms: frozenset[int]) -> None: """Remove intra-molecular interactions for the specified atoms. Args: atoms: The indices of atoms to remove intra-molecular interactions from. """
[docs] @abstractmethod def zero_charges(self, atoms: frozenset[int]) -> None: """Remove charges from the specified atoms. Args: atoms: The indices of atoms to remove charges from. """
[docs] @abstractmethod def zero_intermolecular(self, atoms: frozenset[int]) -> None: """Remove inter-molecular interactions for the specified atoms. Args: atoms: The indices of atoms to remove inter-molecular interactions from. """
[docs] @abstractmethod def zero_forces(self, atoms: frozenset[int]) -> None: """Zero forces on the specified atoms. Args: atoms: The indices of atoms to zero forces for. """
[docs] @abstractmethod def add_real_elst( self, atoms: frozenset[int], const: float | int = 1, inclusion: NDArray[np.float64] | None = None, ) -> None: """Add Coulomb interaction for the specified atoms. Args: atoms: The indices of atoms to add a Coulomb interaction for. const: A constant to multiply at the beginning of the coulomb expression. inclusion: An Nx3 array with values that will be applied to the forces of the Coulomb interaction through element-wise multiplication. """
[docs] @abstractmethod def add_non_elst( self, atoms: frozenset[int], inclusion: NDArray[np.float64] | None = None, ) -> None: """Add a non-electrostatic interaction for the specified atoms. Args: atoms: The indices of atoms to add a non-electrostatic, non-bonded interaction for. inclusion: An Nx3 array with values that will be applied to the forces of the non-electrostatic interaction through element-wise multiplication. """
[docs] class QMInterface(SoftwareInterface): """The abstract QM interface base class. Attributes: theory_level: The level of theory that the software applies in energy and force calculations. """ theory_level = TheoryLevel.QM
[docs] @abstractmethod def disable_embedding(self) -> None: """Disable electrostatic embedding. """