Source code for pyretis.core.forcefieldhelp

# Copyright (c) 2026, PyRETIS Development Team.
# Distributed under the LGPLv2.1+ License. See LICENSE for more info.
"""Forcefield evaluation helpers (system-type-agnostic).

These free functions let callers compute / update the potential and
forces of a :class:`.System` without going through harness-specific
``system.potential_and_force()`` style methods. The functions operate
purely through the System bridge surface (``system.vpot`` / ``system.force``
/ ``system.virial``), so they work transparently on both the harness
System and the snapshot System.

The corresponding harness ``System`` methods delegate to these
helpers; new code should call the free functions directly, since
the harness methods go away in A3.2c.
"""
import logging

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name
logger.addHandler(logging.NullHandler())


__all__ = [
    'update_force',
    'update_potential',
    'update_potential_and_force',
    'evaluate_force',
    'evaluate_potential',
    'evaluate_potential_and_force',
    'rescale_system_velocities',
]


[docs]def update_force(system, forcefield): """Evaluate forces via the forcefield and store on the system. Parameters ---------- system : object like :class:`.System` The system to update. Must expose ``force`` and (optionally) ``virial`` via the bridge surface. forcefield : object like :class:`.ForceField` The forcefield used to evaluate the forces. Returns ------- force : numpy.ndarray The freshly computed force array (also written to ``system.force``). virial : numpy.ndarray The virial tensor (also written to ``system.virial`` when the system carries one). """ force, virial = forcefield.evaluate_force(system) system.force = force if hasattr(system, 'virial'): system.virial = virial return force, virial
[docs]def update_potential(system, forcefield): """Evaluate the potential energy and store it on the system. Parameters ---------- system : object like :class:`.System` The system to update. forcefield : object like :class:`.ForceField` The forcefield used to evaluate the potential. Returns ------- float The potential energy (also written to ``system.vpot``). """ vpot = forcefield.evaluate_potential(system) system.vpot = vpot return vpot
[docs]def update_potential_and_force(system, forcefield): """Evaluate potential, force, and virial and store them on the system. Parameters ---------- system : object like :class:`.System` The system to update. forcefield : object like :class:`.ForceField` The forcefield used. Returns ------- vpot : float The potential energy. force : numpy.ndarray The force array. virial : numpy.ndarray The virial tensor. """ vpot, force, virial = forcefield.evaluate_potential_and_force(system) system.vpot = vpot system.force = force if hasattr(system, 'virial'): system.virial = virial return vpot, force, virial
[docs]def evaluate_force(system, forcefield): """Return forces/virial *without* updating the system. Parameters ---------- system : object like :class:`.System` The system being evaluated. Not mutated. forcefield : object like :class:`.ForceField` The forcefield used. Returns ------- force : numpy.ndarray Force array. virial : numpy.ndarray Virial tensor. """ return forcefield.evaluate_force(system)
[docs]def evaluate_potential(system, forcefield): """Return the potential energy *without* updating the system. Parameters ---------- system : object like :class:`.System` The system being evaluated. Not mutated. forcefield : object like :class:`.ForceField` The forcefield used. Returns ------- float The potential energy. """ return forcefield.evaluate_potential(system)
[docs]def evaluate_potential_and_force(system, forcefield): """Return potential, force, and virial *without* updating the system. Parameters ---------- system : object like :class:`.System` The system being evaluated. Not mutated. forcefield : object like :class:`.ForceField` The forcefield used. Returns ------- vpot : float Potential energy. force : numpy.ndarray Force array. virial : numpy.ndarray Virial tensor. """ return forcefield.evaluate_potential_and_force(system)
[docs]def rescale_system_velocities(system, target_energy, forcefield=None, external=False): """Re-scale ``system.vel`` so total energy matches ``target_energy``. Free-function form of the soon-to-go ``System.rescale_velocities(energy, external=...)`` method. Parameters ---------- system : object like :class:`.System` The system whose velocities should be rescaled in place via the bridge surface (``system.vel`` / ``system.mass`` / ``system.vpot``). target_energy : float Desired total energy (kinetic + potential). forcefield : object like :class:`.ForceField`, optional Required when ``external`` is False; used to recompute ``system.vpot`` before the rescale. Ignored when ``external`` is True (the existing ``system.vpot`` is used). external : bool, optional Mirrors the ``external`` flag of the legacy method. When True, use the cached ``system.vpot``; when False, recompute it via the forcefield first. """ # Local import: rescale_velocities (the lower-level helper) lives # in particlefunctions and indirectly imports core.system through # type hints in older docstrings, so we delay it. from pyretis.core.particlefunctions import rescale_velocities if not external: if forcefield is None: raise ValueError( 'rescale_system_velocities needs a forcefield when ' 'external=False; pass system.forcefield or set ' 'external=True to use the cached vpot.' ) vpot = update_potential(system, forcefield) else: vpot = system.vpot new_vel, ok = rescale_velocities( system.vel, system.mass, vpot, target_energy ) if ok: system.vel = new_vel