Source code for pydft_qmmm.calculators.composite_calculator

"""A calculator that performs and collates sub-calculations.
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

import numpy as np

from .calculator import Calculator
from pydft_qmmm.common import Results

if TYPE_CHECKING:
    from pydft_qmmm import System
    from pydft_qmmm.common import Components
    from pydft_qmmm.plugins.plugin import CalculatorPlugin
    from pydft_qmmm.plugins.plugin import CompositeCalculatorPlugin


[docs] @dataclass class CompositeCalculator(Calculator): """A calculator that performs and collates sub-calculations. Args: system: The system whose atom positions, atom identities, and geometry will be used to calculate energies and forces. calculators: The calculators that will perform sub-calculations. cutoff: The cutoff between regions treated with different levels of theory, comprising the embedding region. """ system: System calculators: list[Calculator] cutoff: float | int = 0. def __post_init__(self) -> None: """Determine the sequence in which to perform sub-calculations. """ self.calculation_sequence = dict() for i, calculator in enumerate(self.calculators): self.calculation_sequence[f"{calculator.name}_{i}"] = calculator
[docs] def calculate( self, return_forces: bool | None = True, return_components: bool | None = True, ) -> Results: r"""Calculate energies and forces. Args: return_forces: Whether or not to return forces. return_components: Whether or not to return the components of the energy. Returns: The energy (:math:`\mathrm{kJ\;mol^{-1}}`), forces (:math:`\mathrm{kJ\;mol^{-1}\;\mathring{A}^{-1}}`), and energy components (:math:`\mathrm{kJ\;mol^{-1}}`) of the calculation. """ energy = 0. forces = np.zeros(self.system.forces.shape) components: Components = dict() for i, (name, calculator) in enumerate( self.calculation_sequence.items(), ): results = calculator.calculate() energy += results.energy forces += results.forces components[name] = results.energy components["."*(i + 1)] = results.components results = Results(energy, forces, components) return results
[docs] def register_plugin( self, plugin: CalculatorPlugin | CompositeCalculatorPlugin, ) -> None: """Record plugin name and apply the plugin to the calculator. Args: plugin: A plugin that will modify the behavior of one or more calculator routines. """ self._plugins.append(type(plugin).__name__) plugin.modify(self)