"""
Contains type definitions for :py:class:`encomp.units.Quantity` objects.
The dimensionalities defined in this module can be combined with ``*`` and ``/``.
Some commonly used derived dimensionalities (like density) are defined for convenience.
Most dimensionalities are backed by unit literals (see :py:func:`get_registered_units`)
and by ``*`` / ``/`` overloads on :py:class:`encomp.units.Quantity`, so they are inferred
statically. A few -- ``NormalTemperature``, ``MassPerEnergy``, ``PowerPerLength``,
``PowerPerVolume``, ``PowerPerTemperature`` and the ``HeatingValue`` family -- are
deliberately *vocabulary only*: they have no unit literals and no overloads, and exist so
user code can annotate quantities with them and reinterpret via
:py:meth:`encomp.units.Quantity.asdim`.
"""
from __future__ import annotations
import logging
from typing import Literal, TypeVar, cast, get_origin
import numpy as np
import polars as pl
from pint.util import UnitsContainer
_LOGGER = logging.getLogger(__name__)
BASE_SI_UNITS = (
"m",
"kg",
"s",
"K",
"mol",
"A",
"cd",
)
# these string literals are used to infer the dimensionality of commonly created quantities
# they are only used by type checkers and ignored at runtime
DimensionlessUnits = Literal[
"",
"%",
"percent",
"pct",
"mol%",
"mol-%",
"mole%",
"mole-%",
"kg%",
"kg-%",
"m3%",
"m3-%",
"m³%",
"m³-%",
"vol%",
"vol-%",
"volume%",
"volume-%",
"wt%",
"wt-%",
"weight%",
"weight-%",
"ppm",
"-",
"dimensionless",
]
# NOTE: the scaling between currencies in defs/units.txt is a fixed placeholder
# (10 SEK = 1 EUR = 1 USD), NOT an exchange rate: converting a Quantity across
# currencies silently applies these fabricated factors. See the Currency
# dimensionality docstring and the "Currency units" section of the docs
CurrencyUnits = Literal[
"SEK",
"EUR",
"USD",
"kSEK",
"kEUR",
"kUSD",
"MSEK",
"MEUR",
"MUSD",
]
CurrencyPerEnergyUnits = Literal[
"SEK/MWh",
"EUR/MWh",
"SEK/kWh",
"EUR/kWh",
"SEK/GWh",
"EUR/GWh",
"SEK/TWh",
"EUR/TWh",
]
CurrencyPerMassUnits = Literal[
"SEK/kg",
"EUR/kg",
"SEK/t",
"EUR/t",
"SEK/ton",
"EUR/ton",
"SEK/g",
"EUR/g",
"SEK/mg",
"EUR/mg",
"SEK/ug",
"EUR/ug",
]
CurrencyPerVolumeUnits = Literal[
"SEK/L",
"EUR/L",
"SEK/l",
"EUR/l",
"SEK/liter",
"EUR/liter",
"SEK/m3",
"EUR/m3",
"SEK/m^3",
"EUR/m^3",
"SEK/m**3",
"EUR/m**3",
"SEK/m³",
"EUR/m³",
]
CurrencyPerTimeUnits = Literal[
"SEK/h",
"EUR/h",
"SEK/hr",
"EUR/hr",
"SEK/hour",
"EUR/hour",
"SEK/d",
"EUR/d",
"SEK/day",
"EUR/day",
"SEK/week",
"EUR/week",
"SEK/yr",
"EUR/yr",
"SEK/year",
"EUR/year",
"SEK/a",
"EUR/a",
]
LengthUnits = Literal[
"m",
"meter",
"km",
"cm",
"mm",
"um",
]
MassUnits = Literal[
"kg",
"g",
"ton",
"tonne",
"t",
"mg",
"ug",
]
TimeUnits = Literal[
"s",
"second",
"min",
"minute",
"h",
"hr",
"hour",
"d",
"day",
"week",
"yr",
"a",
"year",
"ms",
"us",
]
# Frequency is the inverse of Time (1 / [time]); it participates in the reciprocal
# overloads (1 / Time -> Frequency and 1 / Frequency -> Time) so it also gets literal
# unit spellings, letting Q(50, "Hz") infer Frequency statically like any base dimensionality.
FrequencyUnits = Literal[
"Hz",
"kHz",
"MHz",
"GHz",
"rpm",
"1/s",
]
TemperatureUnits = Literal[
"degC",
"°C",
"K",
"degF",
"°F",
"℃",
"℉",
]
TemperatureDifferenceUnits = Literal[
"delta_K",
"ΔK",
"delta_°C",
"delta_degC",
"Δ°C",
"Δ℃",
"delta_°F",
"delta_degF",
"Δ°F",
"Δ℉",
]
SubstanceUnits = Literal[
"mol",
"kmol",
]
MolarMassUnits = Literal[
"g/mol",
"kg/mol",
"kg/kmol",
]
SubstancePerMassUnits = Literal[
"mol/g",
"kmol/kg",
]
CurrentUnits = Literal[
"A",
"mA",
]
LuminosityUnits = Literal["cd", "lm"]
AreaUnits = Literal[
"m2",
"m^2",
"m**2",
"m²",
"cm2",
"cm^2",
"cm**2",
"cm²",
]
VolumeUnits = Literal[
"L",
"l",
"liter",
"m3",
"m^3",
"m³",
"m**3",
"dm3",
"dm^3",
"dm³",
"dm**3",
"cm3",
"cm^3",
"cm³",
"cm**3",
]
NormalVolumeUnits = Literal[
"normal liter",
"Nm3",
"nm3",
"Nm^3",
"nm^3",
"Nm³",
"nm³",
"Nm**3",
"nm**3",
]
PressureUnits = Literal[
"bar",
"kPa",
"Pa",
"MPa",
"mbar",
"mmHg",
"torr",
"psi",
"atm",
"N/m2",
"N/m^2",
"N/m**2",
"N/m²",
]
MassFlowUnits = Literal[
"kg/s",
"kg/h",
"kg/hr",
"g/s",
"g/h",
"g/hr",
"ton/h",
"t/h",
"ton/hr",
"t/hr",
"t/d",
"ton/day",
"ton/week",
"t/a",
"t/year",
"ton/a",
"ton/year",
]
VolumeFlowUnits = Literal[
"m3/s",
"m3/h",
"m3/hr",
"m**3/s",
"m**3/h",
"m**3/hr",
"m^3/s",
"m^3/h",
"m^3/hr",
"m³/s",
"m³/h",
"m³/hr",
"liter/second",
"l/s",
"L/s",
"liter/s",
"liter/hour",
"l/h",
"L/h",
"L/hr",
"l/hr",
]
NormalVolumeFlowUnits = Literal[
"Nm3/s",
"Nm3/h",
"Nm3/hr",
"nm3/s",
"nm3/h",
"nm3/hr",
"Nm^3/s",
"Nm^3/h",
"Nm^3/hr",
"nm^3/s",
"nm^3/h",
"nm^3/hr",
"Nm³/s",
"Nm³/h",
"Nm³/hr",
"nm³/s",
"nm³/h",
"nm³/hr",
"Nm**3/s",
"Nm**3/h",
"Nm**3/hr",
"nm**3/s",
"nm**3/h",
"nm**3/hr",
]
DensityUnits = Literal[
"kg/m3",
"kg/m**3",
"kg/m^3",
"kg/m³",
"kg/liter",
"g/l",
"g/L",
"gram/liter",
"g/cm3",
"g/cm**3",
"g/cm^3",
"g/cm³",
]
MolarDensityUnits = Literal[
"mol/m3",
"mol/m**3",
"mol/m^3",
"mol/m³",
"kmol/m3",
"kmol/m**3",
"kmol/m^3",
"kmol/m³",
"mol/l",
"mol/L",
]
SpecificVolumeUnits = Literal[
"m3/kg",
"m^3/kg",
"m³/kg",
"l/g",
"L/g",
]
NormalVolumePerMassUnits = Literal[
"Nm3/kg",
"Nm^3/kg",
"Nm³/kg",
"nm3/kg",
"nm^3/kg",
"nm³/kg",
]
MassPerNormalVolumeUnits = Literal[
"kg/Nm3",
"kg/Nm^3",
"kg/Nm³",
"kg/nm3",
"kg/nm^3",
"kg/nm³",
"g/Nm3",
"g/Nm^3",
"g/Nm³",
]
EnergyUnits = Literal[
"J",
"kJ",
"MJ",
"GJ",
"TJ",
"PJ",
"kWh",
"MWh",
"Wh",
"GWh",
"TWh",
]
PowerUnits = Literal[
"W",
"kW",
"MW",
"GW",
"TW",
"mW",
"kWh/d",
"kWh/yr",
"kWh/year",
"MWh/d",
"MWh/yr",
"MWh/year",
"GWh/d",
"GWh/yr",
"GWh/year",
"TWh/d",
"TWh/yr",
"TWh/year",
]
VelocityUnits = Literal[
"m/s",
"km/s",
"m/min",
"cm/s",
"cm/min",
"km/h",
"kmh",
"kph",
]
ForceUnits = Literal[
"N",
"kN",
"mN",
]
DynamicViscosityUnits = Literal[
"Pa*s",
"Pa s",
"cP",
]
KinematicViscosityUnits = Literal[
"m2/s",
"m**2/s",
"m^2/s",
"m²/s",
"cSt",
"cm2/s",
"cm**2/s",
"cm^2/s",
"cm²/s",
]
EnergyPerMassUnits = Literal[
"MJ/kg",
"MWh/kg",
"kJ/kg",
"kWh/kg",
"J/kg",
"J/g",
"MJ/t",
"MWh/t",
"kJ/t",
"kWh/t",
"MJ/ton",
"MWh/ton",
"kJ/ton",
"kWh/ton",
]
# CoolProp reports molar enthalpy/internal energy in J/mol
MolarSpecificEnthalpyUnits = Literal[
"J/mol",
"kJ/mol",
"J/kmol",
"kJ/kmol",
"MJ/kmol",
]
SpecificHeatCapacityUnits = Literal[
"kJ/kg/K",
"kJ/kg/delta_degC",
"kJ/kg/Δ°C",
"kJ/kg/Δ℃",
"kJ/kg/°C",
"kJ/kg/℃",
"kJ/kg/degC",
"J/kg/K",
"J/kg/delta_degC",
"J/kg/Δ°C",
"J/kg/Δ℃",
"J/kg/°C",
"J/kg/℃",
"J/kg/degC",
"J/g/K",
"J/g/delta_degC",
"J/g/Δ°C",
"J/g/Δ℃",
"J/g/°C",
"J/g/℃",
"J/g/degC",
]
ThermalConductivityUnits = Literal[
"W/m/K",
"W/m/delta_degC",
"W/m/Δ°C",
"W/m/Δ℃",
"kW/m/K",
"mW/m/K",
]
PowerPerAreaUnits = Literal[
"W/m2",
"W/m^2",
"W/m**2",
"W/m²",
"kW/m2",
"kW/m^2",
"kW/m**2",
"kW/m²",
]
HeatTransferCoefficientUnits = Literal[
"W/m2/K",
"W/m^2/K",
"W/m**2/K",
"W/m²/K",
"W/m2/delta_degC",
"W/m^2/delta_degC",
"W/m**2/delta_degC",
"W/m²/delta_degC",
"W/m2/Δ°C",
"W/m^2/Δ°C",
"W/m**2/Δ°C",
"W/m²/Δ°C",
"kW/m2/K",
"kW/m^2/K",
"kW/m**2/K",
"kW/m²/K",
]
AllUnits = (
DimensionlessUnits
| CurrencyUnits
| CurrencyPerEnergyUnits
| CurrencyPerMassUnits
| CurrencyPerVolumeUnits
| CurrencyPerTimeUnits
| LengthUnits
| MassUnits
| TimeUnits
| FrequencyUnits
| TemperatureUnits
| TemperatureDifferenceUnits
| SubstanceUnits
| MolarMassUnits
| SubstancePerMassUnits
| CurrentUnits
| LuminosityUnits
| AreaUnits
| VolumeUnits
| NormalVolumeUnits
| PressureUnits
| MassFlowUnits
| VolumeFlowUnits
| NormalVolumeFlowUnits
| DensityUnits
| MolarDensityUnits
| SpecificVolumeUnits
| NormalVolumePerMassUnits
| MassPerNormalVolumeUnits
| EnergyUnits
| PowerUnits
| PowerPerAreaUnits
| VelocityUnits
| ForceUnits
| DynamicViscosityUnits
| KinematicViscosityUnits
| EnergyPerMassUnits
| MolarSpecificEnthalpyUnits
| SpecificHeatCapacityUnits
| ThermalConductivityUnits
| HeatTransferCoefficientUnits
)
[docs]
def get_registered_units() -> dict[str, tuple[str, ...]]:
"""Return the literal unit spellings registered for each known dimensionality."""
ret: dict[str, tuple[str, ...]] = {}
for k, v in globals().items():
if get_origin(v) is Literal and k.endswith("Units"):
ret[k.removesuffix("Units")] = v.__args__
return ret
class _DimensionalityMeta(type):
def __eq__(cls, other: object) -> bool:
# qualname-based identity keeps dimensionality classes stable across module
# reloads (e.g. Jupyter autoreload): a re-executed class statement produces
# a new class object that must still compare equal. NOTE: no metaclass /
# issubclass anchor here -- under a reload those module globals rebind to
# the new class objects and would make identical classes compare unequal
if not isinstance(other, type):
return False
return cls.__qualname__ == other.__qualname__
def __hash__(cls) -> int:
# eq/hash contract: __eq__ compares by qualname, so equal (same-qualname)
# classes must hash equal for dict/set correctness. The tag is a plain
# string for the same reload-stability reason as in __eq__
return hash(("encomp.utypes.Dimensionality", cls.__qualname__))
[docs]
class Dimensionality(metaclass=_DimensionalityMeta):
r"""
Represents the *dimensionality* of a unit, i.e.
a combination (product) of the base dimensions (with optional rational exponents).
A dimension can be expressed as
.. math::
\Pi \, d^n_d, d \in \{T, L, M ,I, \Theta, N, J, \ldots\}, n_d \in \mathbb{Q}
where $\{T, L, M, ...\}$ are the base dimensions (time, length, mass, ...)
and $n_d$ is a rational number.
Subclasses of this abstract base class are used
as type parameters when creating instances of
:py:class:`encomp.units.Quantity`.
The ``dimensions`` class attribute defines the dimensions
of the dimensionality using an instance of
``pint.unit.UnitsContainer``.
Distinctness is registry-relative: if another dimensionality with the same
dimensions is registered later, :meth:`is_distinct` for those dimensions may
change. Define custom dimensionalities at import time and use globally
unique class names in library code.
"""
# set _distinct to False for dimensionalities that are not distinct
# purely based on the dimensions
_distinct: bool | None = None
# set to True for intermediate subclasses of Dimensionality
# these cannot be initialized directly, the must be subclassed further
_intermediate: bool = False
# sentinel object to indicate unset UnitsContainer
_UnsetUC = UnitsContainer()
dimensions: UnitsContainer = _UnsetUC
# keeps track of all the dimensionalities that have been
# used in the current process
# use the class definition as key, since multiple dimensionalities
# might have the same UnitsContainer
_registry: dict[type[Dimensionality], UnitsContainer] = {}
# also store a reversed map, this might not contain all items in _registry
# dimensionalities where is_distinct() returns True will have precedence
_registry_reversed: dict[UnitsContainer, type[Dimensionality]] = {}
def __init_subclass__(cls) -> None:
if cls._intermediate:
return
if cls.dimensions is cls._UnsetUC:
raise TypeError(f"Cannot initialize {cls}, class attribute 'dimensions' is not defined for this subclass")
# ensure that the subclass names are unique
if cls.__name__ in (subcls.__name__ for subcls in cls._registry):
existing = next(filter(lambda x: x.__name__ == cls.__name__, cls._registry))
# compare string representations of the UnitsContainer instances
# might run into issues with float accuracy otherwise
# the UnitsContainer.__eq__ method checks hash(frozenset(self._d.items()))
if str(cls.dimensions) != str(existing.dimensions):
raise TypeError(
"Cannot create dimensionality subclass with "
f'name "{cls.__name__}", another subclass with '
"this name already exists and the dimensions do "
f"not match: {cls.dimensions} != {existing.dimensions}"
)
_LOGGER.warning(
"Dimensionality subclass %s with dimensions %s already exists; keeping the first registered class",
cls.__name__,
cls.dimensions,
)
# don't create a new subclass with the same name
return
# make sure a subclass of an existing Dimensionality has the same dimensions
# the first element in __mro__ is the class that is being created, the
# second is the direct parent class
# parent must be either Dimensionality or a subclass
parent = cast(type[Dimensionality], cls.__mro__[1])
# ignore this check if the parent is the base class Dimensionality
if parent.dimensions is not cls._UnsetUC and parent.dimensions != cls.dimensions:
raise TypeError(
f"Cannot create subclass of {parent} where "
"the dimensions do not match. Tried to "
f"create subclass with dimensions {cls.dimensions}, but "
f"the parent has dimensions {parent.dimensions}"
)
# this will never happen,
# since the class name was already checked for duplicates
if cls in cls._registry:
return
cls._registry[cls] = cls.dimensions
# unless specifically overridden with _distinct,
# this will be True only for the first subtype with specific dimensions
if cls.is_distinct():
cls._registry_reversed[cls.dimensions] = cls
[docs]
@classmethod
def get_dimensionality(cls, dimensions: UnitsContainer) -> type[Dimensionality]:
"""Return the registered dimensionality for ``dimensions``.
Unregistered dimensions get a new subclass named after them, for example
``Dimensionality[[mass] ** 2 / [length] ** 3]``.
"""
if dimensions in cls._registry_reversed:
return cls._registry_reversed[dimensions]
# create a new, custom Dimensionality
# not possible to generate a proper name for this,
# so it will just contain the literal dimensions
# this will call __init_subclass__ to register the type
_Dimensionality = cast(
type[Dimensionality],
type(
f"Dimensionality[{dimensions}]",
(Dimensionality,),
{"dimensions": dimensions},
),
)
return _Dimensionality
[docs]
@classmethod
def is_distinct(cls) -> bool:
"""Whether this class is the one :meth:`get_dimensionality` returns for its dimensions.
Unless ``_distinct`` is set explicitly, only the first registered subclass with a
given ``dimensions`` is distinct. The answer is registry-relative and can change
when a later subclass registers the same dimensions.
"""
if cls._distinct is None:
# special case if dimensions was overridden to None
if getattr(cls, "dimensions", None) is None:
return True
ucs = list(cls._registry.values())
# NOTE: the output of this classmethod
# might change when the registry is updated
return ucs.count(cls.dimensions) == 1
return cls._distinct
_DimensionlessUC = UnitsContainer({})
_CurrencyUC = UnitsContainer({"[currency]": 1})
_NormalUC = UnitsContainer({"[normal]": 1})
_LengthUC = UnitsContainer({"[length]": 1})
_MassUC = UnitsContainer({"[mass]": 1})
_TimeUC = UnitsContainer({"[time]": 1})
_TemperatureUC = UnitsContainer({"[temperature]": 1})
_SubstanceUC = UnitsContainer({"[substance]": 1})
_CurrentUC = UnitsContainer({"[current]": 1})
_LuminosityUC = UnitsContainer({"[luminosity]": 1})
Numpy1DArray = np.ndarray[tuple[int], np.dtype[np.float64]]
Numpy1DBoolArray = np.ndarray[tuple[int], np.dtype[np.bool]]
# integer index array, as accepted by numpy "fancy" indexing
Numpy1DIntArray = np.ndarray[tuple[int], np.dtype[np.intp]]
MT = TypeVar(
"MT",
float,
Numpy1DArray,
pl.Series,
pl.Expr,
default=Numpy1DArray,
)
MT_ = TypeVar(
"MT_",
float,
Numpy1DArray,
pl.Series,
pl.Expr,
default=Numpy1DArray,
)
[docs]
class UnknownDimensionality(Dimensionality):
_intermediate = True
# type variables that represent a certain dimensionality
# the DT_ type variable is used to signify a different (possible identical) dimensionality than DT
DT = TypeVar("DT", bound=Dimensionality, default=UnknownDimensionality)
DT_ = TypeVar("DT_", bound=Dimensionality, default=UnknownDimensionality)
[docs]
class Dimensionless(Dimensionality):
dimensions = _DimensionlessUC
[docs]
class Normal(Dimensionality):
dimensions = _NormalUC
[docs]
class Length(Dimensionality):
dimensions = _LengthUC
[docs]
class Mass(Dimensionality):
dimensions = _MassUC
[docs]
class Time(Dimensionality):
dimensions = _TimeUC
[docs]
class Temperature(Dimensionality):
_distinct = True
dimensions = _TemperatureUC
[docs]
class TemperatureDifference(Dimensionality):
dimensions = _TemperatureUC
[docs]
class Substance(Dimensionality):
dimensions = _SubstanceUC
[docs]
class Current(Dimensionality):
dimensions = _CurrentUC
[docs]
class Luminosity(Dimensionality):
dimensions = _LuminosityUC
# derived dimensionalities
_AreaUC = _LengthUC**2
_VolumeUC = _LengthUC**3
_NormalVolumeUC = _VolumeUC * _NormalUC
_PressureUC = _MassUC / _LengthUC / _TimeUC**2
_MassFlowUC = _MassUC / _TimeUC
_VolumeFlowUC = _VolumeUC / _TimeUC
_NormalVolumeFlowUC = _NormalVolumeUC / _TimeUC
_DensityUC = _MassUC / _VolumeUC
_SpecificVolumeUC = 1 / _DensityUC
_EnergyUC = _MassUC * _LengthUC**2 / _TimeUC**2
_PowerUC = _EnergyUC / _TimeUC
_VelocityUC = _LengthUC / _TimeUC
_ForceUC = _MassUC * _LengthUC / _TimeUC**2
_DynamicViscosityUC = _MassUC / _LengthUC / _TimeUC
_KinematicViscosityUC = _LengthUC**2 / _TimeUC
_FrequencyUC = 1 / _TimeUC
_MolarMassUC = _MassUC / _SubstanceUC
_MolarDensityUC = _SubstanceUC / _VolumeUC
_CurrencyPerEnergyUC = _CurrencyUC / _EnergyUC
_CurrencyPerMassUC = _CurrencyUC / _MassUC
_CurrencyPerVolumeUC = _CurrencyUC / _VolumeUC
_CurrencyPerTimeUC = _CurrencyUC / _TimeUC
_PowerPerLengthUC = _PowerUC / _LengthUC
_PowerPerAreaUC = _PowerUC / _AreaUC
_PowerPerVolumeUC = _PowerUC / _VolumeUC
_PowerPerTemperatureC = _PowerUC / _TemperatureUC
_ThermalConductivityUC = _PowerUC / _LengthUC / _TemperatureUC
_HeatTransferCoefficientUC = _PowerUC / _AreaUC / _TemperatureUC
_MassPerNormalVolumeUC = _MassUC / _NormalVolumeUC
_MassPerEnergyUC = _MassUC / _EnergyUC
_MolarSpecificEntropyUC = _EnergyUC / _TemperatureUC / _SubstanceUC
_SurfaceTensionUC = _ForceUC / _LengthUC
[docs]
class Area(Dimensionality):
dimensions = _AreaUC
[docs]
class Volume(Dimensionality):
dimensions = _VolumeUC
[docs]
class NormalVolume(Dimensionality):
dimensions = _NormalVolumeUC
[docs]
class Pressure(Dimensionality):
dimensions = _PressureUC
[docs]
class MassFlow(Dimensionality):
dimensions = _MassFlowUC
[docs]
class VolumeFlow(Dimensionality):
dimensions = _VolumeFlowUC
[docs]
class NormalVolumeFlow(Dimensionality):
dimensions = _NormalVolumeFlowUC
[docs]
class Density(Dimensionality):
dimensions = _DensityUC
[docs]
class SpecificVolume(Dimensionality):
dimensions = _SpecificVolumeUC
[docs]
class Energy(Dimensionality):
dimensions = _EnergyUC
[docs]
class Power(Dimensionality):
dimensions = _PowerUC
[docs]
class Velocity(Dimensionality):
dimensions = _VelocityUC
[docs]
class Force(Dimensionality):
dimensions = _ForceUC
[docs]
class DynamicViscosity(Dimensionality):
dimensions = _DynamicViscosityUC
[docs]
class KinematicViscosity(Dimensionality):
dimensions = _KinematicViscosityUC
[docs]
class Frequency(Dimensionality):
dimensions = _FrequencyUC
[docs]
class MolarMass(Dimensionality):
dimensions = _MolarMassUC
[docs]
class SubstancePerMass(Dimensionality):
dimensions = 1 / _MolarMassUC
[docs]
class MolarDensity(Dimensionality):
dimensions = _MolarDensityUC
[docs]
class Currency(Dimensionality):
"""
Arbitrary currency (``[currency]`` dimension).
.. warning::
The scaling between the built-in currencies (``SEK``, ``EUR``, ``USD``)
is a fixed placeholder (``10 SEK = 1 EUR = 1 USD``), *not* an exchange
rate: converting a Quantity across currencies silently applies these
fabricated factors. Do not use this system for currency conversion --
keep all quantities in a single currency, or implement a pint context
with real exchange rates.
"""
dimensions = _CurrencyUC
[docs]
class CurrencyPerEnergy(Dimensionality):
dimensions = _CurrencyPerEnergyUC
[docs]
class CurrencyPerMass(Dimensionality):
dimensions = _CurrencyPerMassUC
[docs]
class CurrencyPerVolume(Dimensionality):
dimensions = _CurrencyPerVolumeUC
[docs]
class CurrencyPerTime(Dimensionality):
dimensions = _CurrencyPerTimeUC
[docs]
class PowerPerLength(Dimensionality):
dimensions = _PowerPerLengthUC
[docs]
class PowerPerArea(Dimensionality):
dimensions = _PowerPerAreaUC
[docs]
class PowerPerVolume(Dimensionality):
dimensions = _PowerPerVolumeUC
[docs]
class PowerPerTemperature(Dimensionality):
dimensions = _PowerPerTemperatureC
[docs]
class ThermalConductivity(Dimensionality):
dimensions = _ThermalConductivityUC
[docs]
class HeatTransferCoefficient(Dimensionality):
dimensions = _HeatTransferCoefficientUC
[docs]
class MassPerNormalVolume(Dimensionality):
dimensions = _MassPerNormalVolumeUC
[docs]
class MassPerEnergy(Dimensionality):
dimensions = _MassPerEnergyUC
[docs]
class SurfaceTension(Dimensionality):
dimensions = _SurfaceTensionUC
[docs]
class MolarSpecificEntropy(Dimensionality):
dimensions = _MolarSpecificEntropyUC
# not called "SpecificNormalVolume" since that term is not commonly used
# this name is more explicit
[docs]
class NormalVolumePerMass(Dimensionality):
dimensions = 1 / _MassPerNormalVolumeUC
[docs]
class NormalTemperature(Dimensionality):
dimensions = _TemperatureUC * _NormalUC
# these dimensionalities might have different names depending on the context
# they are not included as inputs for the autogenerated type hint
# (unless the _distinct class attribute is set to True)
[docs]
class EnergyPerMass(Dimensionality):
_distinct = True
dimensions = _EnergyUC / _MassUC
# don't consider SpecificEnthalpy separate from EnergyPerMass
SpecificEnthalpy = EnergyPerMass
[docs]
class HeatingValue(Dimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class LowerHeatingValue(Dimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class HigherHeatingValue(Dimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class SpecificInternalEnergy(Dimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class MolarSpecificEnthalpy(Dimensionality):
_distinct = True
dimensions = _EnergyUC / _SubstanceUC
[docs]
class MolarSpecificInternalEnergy(Dimensionality):
dimensions = _EnergyUC / _SubstanceUC
# shares its dimensions with the (earlier, and therefore distinct) MolarSpecificEntropy,
# so a unit like J/mol/K still resolves to MolarSpecificEntropy at runtime
[docs]
class MolarSpecificHeatCapacity(Dimensionality):
dimensions = _MolarSpecificEntropyUC
[docs]
class SpecificHeatCapacity(Dimensionality):
_distinct = True
dimensions = _EnergyUC / _MassUC / _TemperatureUC
[docs]
class SpecificEntropy(Dimensionality):
dimensions = _EnergyUC / _MassUC / _TemperatureUC
# related to CoolProp humid air
# these dimensionalities are not distinct, the same
# combination of dimensions can be mean multiple things
[docs]
class IndistinctDimensionality(Dimensionality):
_intermediate = True
_distinct = False
[docs]
class SpecificHeatPerDryAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC / _TemperatureUC
[docs]
class SpecificHeatPerHumidAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC / _TemperatureUC
[docs]
class MixtureEnthalpyPerDryAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class MixtureEnthalpyPerHumidAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC
[docs]
class MixtureEntropyPerDryAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC / _TemperatureUC
[docs]
class MixtureEntropyPerHumidAir(IndistinctDimensionality):
dimensions = _EnergyUC / _MassUC / _TemperatureUC
[docs]
class MixtureVolumePerDryAir(IndistinctDimensionality):
dimensions = _VolumeUC / _MassUC
[docs]
class MixtureVolumePerHumidAir(IndistinctDimensionality):
dimensions = _VolumeUC / _MassUC
__all__ = [
"BASE_SI_UNITS",
"DT",
"DT_",
"MT",
"MT_",
"AllUnits",
"Area",
"AreaUnits",
"Currency",
"CurrencyPerEnergy",
"CurrencyPerEnergyUnits",
"CurrencyPerMass",
"CurrencyPerMassUnits",
"CurrencyPerTime",
"CurrencyPerTimeUnits",
"CurrencyPerVolume",
"CurrencyPerVolumeUnits",
"CurrencyUnits",
"Current",
"CurrentUnits",
"Density",
"DensityUnits",
"Dimensionality",
"Dimensionless",
"DimensionlessUnits",
"DynamicViscosity",
"DynamicViscosityUnits",
"Energy",
"EnergyPerMass",
"EnergyPerMassUnits",
"EnergyUnits",
"Force",
"ForceUnits",
"Frequency",
"FrequencyUnits",
"HeatTransferCoefficient",
"HeatTransferCoefficientUnits",
"HeatingValue",
"HigherHeatingValue",
"IndistinctDimensionality",
"KinematicViscosity",
"KinematicViscosityUnits",
"Length",
"LengthUnits",
"LowerHeatingValue",
"Luminosity",
"LuminosityUnits",
"Mass",
"MassFlow",
"MassFlowUnits",
"MassPerEnergy",
"MassPerNormalVolume",
"MassPerNormalVolumeUnits",
"MassUnits",
"MixtureEnthalpyPerDryAir",
"MixtureEnthalpyPerHumidAir",
"MixtureEntropyPerDryAir",
"MixtureEntropyPerHumidAir",
"MixtureVolumePerDryAir",
"MixtureVolumePerHumidAir",
"MolarDensity",
"MolarDensityUnits",
"MolarMass",
"MolarMassUnits",
"MolarSpecificEnthalpy",
"MolarSpecificEnthalpyUnits",
"MolarSpecificEntropy",
"MolarSpecificHeatCapacity",
"MolarSpecificInternalEnergy",
"Normal",
"NormalTemperature",
"NormalVolume",
"NormalVolumeFlow",
"NormalVolumeFlowUnits",
"NormalVolumePerMass",
"NormalVolumePerMassUnits",
"NormalVolumeUnits",
"Numpy1DArray",
"Numpy1DBoolArray",
"Numpy1DIntArray",
"Power",
"PowerPerArea",
"PowerPerAreaUnits",
"PowerPerLength",
"PowerPerTemperature",
"PowerPerVolume",
"PowerUnits",
"Pressure",
"PressureUnits",
"SpecificEnthalpy",
"SpecificEntropy",
"SpecificHeatCapacity",
"SpecificHeatCapacityUnits",
"SpecificHeatPerDryAir",
"SpecificHeatPerHumidAir",
"SpecificInternalEnergy",
"SpecificVolume",
"SpecificVolumeUnits",
"Substance",
"SubstancePerMass",
"SubstancePerMassUnits",
"SubstanceUnits",
"SurfaceTension",
"Temperature",
"TemperatureDifference",
"TemperatureDifferenceUnits",
"TemperatureUnits",
"ThermalConductivity",
"ThermalConductivityUnits",
"Time",
"TimeUnits",
"UnitsContainer",
"UnknownDimensionality",
"Velocity",
"VelocityUnits",
"Volume",
"VolumeFlow",
"VolumeFlowUnits",
"VolumeUnits",
"get_registered_units",
]