r"""
Unit conversion and mathematical operation functions.
Mathematical Formulation:
1. Temperature Conversions:
a) Kelvin to Celsius:
.. math::
T_{C} = T_{K} - 273.15
where:
- :math:`T_{C}` is temperature in Celsius
- :math:`T_{K}` is temperature in Kelvin
b) Celsius to Kelvin:
.. math::
T_{K} = T_{C} + 273.15
2. Mathematical Operations:
a) Multiplication by Constant:
.. math::
y = x \cdot c
where :math:`c` is the constant factor
b) Regularization:
.. math::
y = \max(x, l)
where :math:`l` is the lower limit
c) Addition:
.. math::
y = \sum_{i=1}^{n} f_i(x_i)
where :math:`f_i` are conversion functions
d) Multiplication:
.. math::
y = \prod_{i=1}^{n} f_i(x_i)
e) Integration:
.. math::
y(t) = y(t-\Delta t) + f(x(t)) \cdot \Delta t
where:
- :math:`y(t)` is the integrated value at time t
- :math:`\Delta t` is the time step
- :math:`f(x(t))` is the conversion function
"""
# Local application imports
from twin4build.utils.rgetattr import rgetattr
[docs]
def do_nothing(x, step_size=None):
return x
[docs]
def change_sign(x, step_size=None):
return -x
[docs]
def to_degC_from_degK(K, step_size=None):
return K - 273.15
[docs]
def to_degK_from_degC(C, step_size=None):
return C + 273.15
[docs]
class multiply_const:
def __init__(self, factor):
self.factor = factor
[docs]
def call(self, x, step_size=None):
return x * self.factor
__call__ = call
[docs]
class regularize:
def __init__(self, limit):
self.limit = limit
[docs]
def call(self, x, step_size=None):
return self.limit if x < self.limit else x
__call__ = call
[docs]
class add_attr:
def __init__(self, obj, attr):
self.obj = obj
self.attr = attr
[docs]
def call(self, x, step_size=None):
return x + rgetattr(self.obj, self.attr)
__call__ = call
[docs]
class add:
def __init__(self, obj, idx, conversion=do_nothing):
if isinstance(idx, tuple) == False:
idx = (idx,)
if isinstance(obj, tuple) == False:
obj = tuple([obj for i in idx])
self.obj = obj
self.idx = idx
self.conversion = conversion
[docs]
def call(self, x, step_size=None):
y = 0
for obj_, idx_ in zip(self.obj, self.idx):
y += self.conversion(obj_[idx_])
return y
__call__ = call
[docs]
class multiply:
def __init__(self, obj, idx, conversion=do_nothing):
if isinstance(idx, tuple) == False:
idx = (idx,)
if isinstance(obj, tuple) == False:
obj = tuple([obj for i in idx])
self.obj = obj
self.idx = idx
self.conversion = conversion
[docs]
def call(self, x, step_size=None):
y = 1
for obj_, idx_ in zip(self.obj, self.idx):
y *= self.conversion(obj_[idx_])
return y
__call__ = call
[docs]
class get:
def __init__(self, obj, idx, conversion=do_nothing):
self.obj = obj
self.idx = idx
self.conversion = conversion
[docs]
def call(self, x, step_size=None):
return self.conversion(self.obj[self.idx])
__call__ = call
[docs]
class integrate:
def __init__(self, obj, idx, conversion=do_nothing):
self.v = 0
self.obj = obj
self.idx = idx
self.conversion = conversion
[docs]
def call(self, x, step_size=None):
self.v += self.conversion(self.obj[self.idx]) * step_size
return self.v
__call__ = call