Source code for tespy.tools.units

# -*- coding: utf-8

"""Module for Units class.

This file is part of project TESPy (github.com/oemof/tespy). It's copyrighted
by the contributors recorded in the version control history of the file,
available from its original location tespy/tools/units.py

SPDX-License-Identifier: MIT
"""
import shutil
import sys
import warnings

import pint
import platformdirs

from tespy import __datapath__


[docs] class Units:
[docs] @classmethod def from_json(cls, default_units): instance = cls() instance.set_defaults(**default_units) return instance
def __init__(self): self.default = { "temperature": "kelvin", "temperature_difference": "delta_degC", "enthalpy": "J/kg", "specific_energy": "J/kg", "entropy": "J/kg/K", "pressure": "Pa", "mass_flow": "kg/s", "volumetric_flow": "m3/s", "specific_volume": "m3/kg", "power": "W", "heat": "W", "quality": "1", "vapor_mass_fraction": "1", # backwards compatibility network import "efficiency": "1", "ratio": "1", "length": "m", "speed": "m/s", "area": "m2", "thermal_conductivity": "W/m/K", "heat_transfer_coefficient": "W/K", "angle": "degree", # the SI unit for angle would be radians, but that would break things in the compressor "frequency": "1/s", # None is the default if not quantity is supplied None: "1" } # necessary, because pint cannot auto detect environment changes and # pint version changes major = sys.version_info.major minor = sys.version_info.minor path = platformdirs.user_cache_dir( "tespy", False, f"py{major}{minor}pint{pint.__version__}" ) try: self._ureg = pint.UnitRegistry(cache_folder=path) except FileNotFoundError: # this is necessary, because inside the cache folder, pint points # to the pint installation inside (any) of the venvs (potentially # the first ever created?). If that venv moves or gets deleted, # then the link cannot be found any more and we have to recreated # the cache shutil.rmtree(path) self._ureg = pint.UnitRegistry(cache_folder=path) # cannot use the setter here because we have to define m3 first! self.ureg.define("m3 = m ** 3") self.ureg.define("m2 = m ** 2") self.ureg.define("kgK = kg * K") self._quantities = { k: self.ureg.Quantity(1, v) for k, v in self.default.items() }
[docs] def set_defaults(self, **kwargs): """Set the default units Parameters ---------- temperature : str Default unit: "kelvin" temperature_difference : str Default unit: "delta_degC" enthalpy : str Default unit: "J/kg" specific_energy : str Default unit: "J/kg" entropy : str Default unit: "J/kg/K" pressure : str Default unit: "Pa" mass_flow : str Default unit: "kg/s" volumetric_flow : str Default unit: "m3/s" specific_volume : str Default unit: "m3/kg" power : str Default unit: "W" heat : str Default unit: "W" quality : str Default unit: "1" efficiency : str Default unit: "1" ratio : str Default unit: "1" length : str Default unit: "m" speed : str Default unit: "m/s" area : str Default unit: "m2" thermal_conductivity : str Default unit: "W/m/K" heat_transfer_coefficient : str Default unit: "W/K" """ for key, value in kwargs.items(): self._check_quantity_exists(key) if value == "-": value = "1" elif value == "C": value = "degC" msg = ( "The unit 'C' is used for 'Coulomb' in pint. For " "backwards compatibility it will be parsed as degC for " "now. Please use '°C' (or correct pint aliases) instead " "as it will stop working with the next major release" ) warnings.warn(msg, FutureWarning) if self._is_compatible(key, value): self.default[key] = value else: msg = f"Unit {value} is not compatible with quantity {key}" raise ValueError(msg)
def _is_compatible(self, quantity, unit): if quantity == "temperature_difference": if unit.startswith("delta_"): return self._quantities[quantity].is_compatible_with(unit) else: _units = self.ureg._units kelvin = list(_units["K"].aliases) + [_units["K"].name] rankine = list(_units["rankine"].aliases) + [_units["rankine"].name] return unit in kelvin or unit in rankine else: return self._quantities[quantity].is_compatible_with(unit)
[docs] def get_default(self, quantity): self._check_quantity_exists(quantity) return self.default[quantity]
def _check_quantity_exists(self, quantity): if quantity not in self.default: msg = ( f"The quantity {quantity} is unknown. Please specify any of " f"the following: " f"{', '.join([key for key in self.default if key is not None])}." ) raise KeyError(msg)
[docs] def set_ureg(self, ureg): """Replace default ureg with a custom one Parameters ---------- ureg : pint.UnitRegistry Change the pint.UnitRegistry to a custom one """ self._ureg = ureg self.ureg.define("m3 = m ** 3") self.ureg.define("m2 = m ** 2") self._quantities = { k: self.ureg.Quantity(1, v) for k, v in self.default.items() }
[docs] def get_ureg(self): return self._ureg
def _serialize(self): return {k: v for k, v in self.default.items() if k is not None} ureg = property(get_ureg, set_ureg)
_UNITS = Units() SI_UNITS = _UNITS.default.copy()