Source code for pyretis.core.systemhelp

# Copyright (c) 2026, PyRETIS Development Team.
# Distributed under the LGPLv2.1+ License. See LICENSE for more info.
"""System helpers that don't involve the forcefield.

Pure-lookup helpers that work on both the harness System and the
snapshot System. They cover the small bag of legacy
``system.method()`` calls (dimensionality, Boltzmann lookup, box
update) that aren't worth attaching to either System class -- the
free-function form means we don't have to grow the snapshot's API
just to satisfy A3.2c's "delete the harness" goal.
"""
import logging
import numpy as np

from pyretis.core.box import create_box
from pyretis.core.particlefunctions import calculate_kinetic_temperature
from pyretis.core.random_gen import create_random_generator
from pyretis.core.units import CONSTANTS

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


__all__ = [
    'get_system_dim',
    'get_boltzmann',
    'update_system_box',
    'generate_system_velocities',
    'calculate_system_temperature',
]


[docs]def get_system_dim(system): """Return the spatial dimensionality of the system. Works for both the harness System (``system.box`` is a :class:`.Box` with a ``.dim`` attribute) and the snapshot System (``system.box`` is a 3x3 ``np.ndarray``). Parameters ---------- system : object like :class:`.System` Returns ------- int Spatial dimensionality (1, 2, or 3). Falls back to ``1`` when no box is attached -- matches the legacy :meth:`.System.get_dim` fallback. """ box = getattr(system, 'box', None) if box is None: logger.warning( 'Box dimensions are not set. Setting dimensions to "1"' ) return 1 if hasattr(box, 'dim'): return box.dim if isinstance(box, np.ndarray): return box.shape[0] # Last-resort: try ``len``. return len(box)
[docs]def get_boltzmann(system): """Return the Boltzmann constant in the system's unit system. Parameters ---------- system : object like :class:`.System` Must expose a ``units`` attribute (string key into :data:`pyretis.core.units.CONSTANTS`). Returns ------- float Boltzmann constant for ``system.units``. """ return CONSTANTS['kB'][system.units]
[docs]def update_system_box(system, length): """Update the system box, creating one if needed. Mirrors the legacy ``System.update_box(length)`` method on the harness System; on the snapshot side it overwrites the 3x3 matrix. Parameters ---------- system : object like :class:`.System` length : numpy.ndarray, list, or iterable Box vectors / cell description. """ box = getattr(system, 'box', None) if box is None or isinstance(box, np.ndarray): # Snapshot System (or harness with no box yet) -- build a # fresh harness Box. Callers running against the snapshot may # later replace this with a snapshot-native box helper. system.box = create_box(cell=length) else: box.update_size(length)
[docs]def generate_system_velocities(system, rgen=None, seed=0, momentum=True, temperature=None, distribution='maxwell'): """Generate per-particle velocities at the system temperature. Free-function form of the soon-to-go :meth:`.System.generate_velocities`. Parameters ---------- system : object like :class:`.System` Must have ``system.particles``, ``system.units``, and ``system.temperature['set' / 'dof']`` populated. rgen : str or object, optional Random generator selector / instance. seed : int, optional Seed for the random generator. momentum : bool, optional If True, reset the linear momentum after drawing. temperature : float, optional Override target temperature. When None, taken from ``system.temperature['set']``. distribution : str, optional Currently only ``'maxwell'`` is supported. """ # Argument list mirrors the legacy System.generate_velocities API. # pylint: disable=too-many-arguments,too-many-positional-arguments rgen_settings = {'seed': seed, 'rgen': rgen} rgen = create_random_generator(rgen_settings) if temperature is None: temperature = system.temperature['set'] dof = system.temperature['dof'] if distribution.lower() == 'maxwell': rgen.generate_maxwellian_velocities( system.particles, CONSTANTS['kB'][system.units], temperature, dof, momentum=momentum, ) else: logger.error( 'Distribution "%s" not defined! Velocities not set!', distribution, )
[docs]def calculate_system_temperature(system): """Compute the current temperature from particle velocities. Free-function form of the soon-to-go :meth:`.System.calculate_temperature`. Parameters ---------- system : object like :class:`.System` Returns ------- float Kinetic temperature. """ dof = system.temperature['dof'] _, temp, _ = calculate_kinetic_temperature( system.particles, CONSTANTS['kB'][system.units], dof=dof, ) return temp