Source code for thermochemistry_step.hessian

# -*- coding: utf-8 -*-

"""The energy and Hessian of a structure from the flowchart's Model Chemistry.

A thin mixin over :class:`seamm_exec.hessian.HessianDriver`, which holds the
route choice (warm MDI engine or tasks; analytic or finite differences), the
engine's environment and cores, and the optimization over the engine. The
driver moved to seamm_exec on 2026-11-05 so that the Thermochemistry, Normal
Mode Sampling and Vibrational Analysis steps share one copy.
"""

import logging

import seamm_util.printing as printing

# The helpers moved with the driver; imported here for older callers.
from seamm_exec.hessian import (  # noqa: F401
    HessianDriver,
    _accepts_keyword,
    module_environment,
    modules_to_load,
    parse_cpu_ids,
    physical_cores_allowed,
    slurm_allocated_cpus,
)

logger = logging.getLogger(__name__)
printer = printing.getPrinter("Thermochemistry")


[docs] class ModelChemistryHessian: """Mixin: the energy and Cartesian Hessian from the Model Chemistry. The route is the step's ``_hessian_route`` ("automatic", "warm engine over MDI" or "separate tasks"); the engine's settings fingerprints from the last calculation are in ``_fingerprints``. """
[docs] def model_chemistry(self): """The ``_model_chemistry`` wrapper: a preset's own (see the CBH approach's presets), else the one published upstream, or None.""" preset = getattr(self, "_preset_mc", None) if preset is not None: return preset if not self.variable_exists("_model_chemistry"): return None return self.get_variable("_model_chemistry")
def _hessian_driver(self): return HessianDriver( self, self.model_chemistry(), route=getattr(self, "_hessian_route", "automatic") or "automatic", who="Thermochemistry", hint="or put the calculation in this step's subflowchart", print_to=printer, )
[docs] def model_chemistry_energy(self, configuration): """The energy (kJ/mol) of the configuration with the Model Chemistry.""" return self._hessian_driver().energy(configuration)
[docs] def energy_and_hessian(self, configuration, delta_bohr=0.01): """The energy (kJ/mol), the Cartesian Hessian (hartree/bohr², (3N, 3N)) and how it was obtained: "analytic", "finite differences over MDI" or "finite differences as tasks".""" return self.optimize_and_hessian(configuration, delta_bohr, optimize=False)
[docs] def optimize_and_energy( self, configuration, optimize=True, fmax=0.005, max_steps=200 ): """Optionally optimize the structure over the warm engine, then its energy (kJ/mol). With no engine (tasks) the energy only.""" driver = self._hessian_driver() try: return driver.optimize_and_energy(configuration, optimize, fmax, max_steps) finally: self._fingerprints = driver.fingerprints self._last_optimization = getattr(driver, "last_optimization", None)
[docs] def optimize_and_hessian( self, configuration, delta_bohr=0.01, optimize=True, fmax=0.005, max_steps=200 ): """Optionally optimize the structure (ASE BFGS over the warm engine), then the energy and Hessian, in one engine session. Returns ------- (float, numpy.ndarray, str) As `energy_and_hessian`. """ driver = self._hessian_driver() try: return driver.optimize_and_hessian( configuration, delta_bohr, optimize, fmax, max_steps ) finally: self._fingerprints = driver.fingerprints self._last_optimization = getattr(driver, "last_optimization", None)