Source code for openff.evaluator.thermodynamics

"""
Defines an API for defining thermodynamic states.
"""
from enum import Enum

from openff.units import unit

from openff.evaluator.attributes import UNDEFINED, Attribute, AttributeClass


class Ensemble(Enum):
    """An enum describing the supported thermodynamic ensembles."""

    NVT = "NVT"
    NPT = "NPT"


[docs]class ThermodynamicState(AttributeClass): """Data specifying a physical thermodynamic state obeying Boltzmann statistics. Notes ----- Equality of two thermodynamic states is determined by comparing the temperature in kelvin to within 3 decimal places, and comparing the pressure (if defined) in pascals to within 3 decimal places. Examples -------- Specify an NPT state at 298 K and 1 atm pressure. >>> state = ThermodynamicState(temperature=298.0*unit.kelvin, pressure=1.0*unit.atmospheres) Note that the pressure is only relevant for periodic systems. """ temperature = Attribute( docstring="The external temperature.", type_hint=unit.Quantity ) pressure = Attribute( docstring="The external pressure.", type_hint=unit.Quantity, optional=True ) @property def inverse_beta(self): """Returns the temperature multiplied by the molar gas constant""" return (self.temperature * unit.molar_gas_constant).to( unit.kilojoule / unit.mole ) @property def beta(self): """Returns one divided by the temperature multiplied by the molar gas constant""" return 1.0 / self.inverse_beta
[docs] def __init__(self, temperature=None, pressure=None): """Constructs a new ThermodynamicState object. Parameters ---------- temperature : openff.evaluator.unit.Quantity The external temperature pressure : openff.evaluator.unit.Quantity The external pressure """ if temperature is not None: self.temperature = temperature if pressure is not None: self.pressure = pressure
[docs] def validate(self, attribute_type=None): super(ThermodynamicState, self).validate(attribute_type) if self.pressure != UNDEFINED: self.pressure.to(unit.pascals) assert self.pressure > 0.0 * unit.pascals self.temperature.to(unit.kelvin) assert self.temperature > 0.0 * unit.kelvin
def __repr__(self): return f"<ThermodynamicState {str(self)}>" def __str__(self): return_value = f"T={self.temperature:~}" if self.pressure != UNDEFINED: return_value += f" P={self.pressure:~}" return return_value def __hash__(self): temperature = self.temperature.to(unit.kelvin).magnitude pressure = ( None if self.pressure == UNDEFINED else self.pressure.to(unit.pascal).magnitude ) return hash( (f"{temperature:.3f}", None if pressure is None else f"{pressure:.3f}") ) def __eq__(self, other): if not isinstance(other, ThermodynamicState): return False return hash(self) == hash(other) def __ne__(self, other): return not (self == other)