#
# Variable conventions shared by all models
#
"""
Standard variable names (DESIGN D12) and the helper that generates unit twins.
Models define every quantity in SI units (K, W, J, ...). For reading convenience, every
model also provides twins in common units, generated by :func:`add_unit_twins` when the
model is built and never written by hand (see :data:`UNIT_TWINS`): a temperature in [K]
gets a [degC] twin, a power in [W] a [kW] twin, an energy in [J] a [kWh] twin, a time in
[s] [min] and [h] twins, a volume flow rate in [m3.s-1] a [l.min-1] twin and a volume in
[m3] a [l] twin. Temperature differences have no twin (a difference of 5 K is
not 5 degC): their names contain "temperature difference" (e.g. "Inlet-outlet temperature
difference [K]").
"""
#: Variables every model must provide
REQUIRED_VARIABLES = (
"Time [s]",
"Mass flow rate [kg.s-1]",
"Inlet temperature [K]",
"Outlet temperature [K]",
"Inlet specific enthalpy [J.kg-1]",
"Outlet specific enthalpy [J.kg-1]",
"Power [W]",
"Discharged energy [J]",
"Charged energy [J]",
"Discharged throughput [J]",
"Charged throughput [J]",
"Storage thermal energy [J]",
"Storage average temperature [K]",
"Storage cross-section average temperature [K]",
"Heat transfer fluid thermal energy [J]",
"Total thermal energy [J]",
"Heat loss power [W]",
"Heat transfer fluid specific enthalpy [J.kg-1]",
"Heat transfer fluid temperature [K]",
"Heat transfer fluid velocity [m.s-1]",
"Inlet-outlet temperature difference [K]",
"Volume flow rate [m3.s-1]",
"Pumped volume [m3]",
)
#: Variables a model may provide, under these names when it does
OPTIONAL_VARIABLES = ("Average liquid fraction",)
#: Names reserved for quantities no model computes yet, so that the first ones agree
RESERVED_VARIABLES = ("Pressure drop [Pa]",)
#: Name of the flow-path coordinate in every family
FLOW_COORDINATE = "z"
#: Name of the flow-path domain in every family
FLOW_DOMAIN = "flow path"
#: The unit twins of a variable in an SI unit (key), each with its unit and conversion
UNIT_TWINS = {
"[K]": (("[degC]", lambda T: T - 273.15),),
"[W]": (("[kW]", lambda P: P / 1e3),),
"[J]": (("[kWh]", lambda E: E / 3.6e6),),
"[s]": (("[min]", lambda t: t / 60), ("[h]", lambda t: t / 3600)),
"[m3.s-1]": (("[l.min-1]", lambda V: V * 6e4),),
"[m3]": (("[l]", lambda V: V * 1e3),),
}
[docs]
def is_temperature_difference(name):
"""Whether a variable name denotes a temperature difference (K only, no twin)"""
return "temperature difference" in name.lower()
[docs]
def unit_twins(name):
"""
A variable's unit twins, as ``{twin name: conversion}``; empty if it has none (its unit
is not in :data:`UNIT_TWINS`, or it is a temperature difference)
"""
if is_temperature_difference(name):
return {}
for unit, twins in UNIT_TWINS.items():
if name.endswith(unit):
stem = name[: -len(unit)]
return {stem + twin_unit: convert for twin_unit, convert in twins}
return {}
[docs]
def add_unit_twins(variables):
"""
Add the unit twin of every variable that has one (see :data:`UNIT_TWINS`).
Parameters
----------
variables : dict
The variables, in SI units (modified in place).
Returns
-------
dict
``variables``, for chaining.
Raises
------
ValueError
If a twin is already defined: twins are generated, never written by hand.
"""
twin_units = tuple(unit for twins in UNIT_TWINS.values() for unit, _ in twins)
by_hand = [name for name in variables if name.endswith(twin_units)]
if by_hand:
raise ValueError(
f"Variables in non-SI units must not be written by hand (they are generated "
f"as unit twins): {by_hand}"
)
for name in list(variables):
for twin, convert in unit_twins(name).items():
variables[twin] = convert(variables[name])
return variables