Quantity related functionality¶
This module defines classes and functionality around pint quantities.
These quantities can be symbolic (hosting casadi.SX as magnitudes), or
numeric.
Objects¶
Quantity¶
SymbolQuantity¶
- class simu.SymbolQuantity¶
A quantity class specialised to host casadi symbols (SX) of in particular, but not necessarily, independent variables.
- static __new__(cls, *args, **kwargs)¶
Really generate an object of type
Quantity. This is just a hacky way to specialise the constructor, due to the way the base-class is implemented. The arguments are:name(str): The name of thecasadi.SXsymbolunits(str): The unit of measurement conform topintunitssub_keys(Iterator[str]): If notNone, defines a vectorial quantity with given sub-keys.>>> s = SymbolQuantity("speed", "m/s") >>> print(f"{s:~}") speed m / s
>>> v = SymbolQuantity("vel", "m/s", "xyz") >>> print(f"{v:~}") [vel.x, vel.y, vel.z] m / s
QFunction¶
- class simu.QFunction¶
Wrapper around
casadi.Functionto consider units of measurements. This derived function object is defined by a dictionary of arguments and results, both of which areQuantityinstances with casadi symbols as magnitudes. These symbols are independent symbols forargsand derived symbols forresults.The function object is then called as well with a dictionary of
Quantityvalues. The units of measurement must be consistent, and a conversion is done to the initially defined units for the individual arguments. The result is given back as a dictionary ofQuantityvalues in the same units as initially defined.- __init__(
- args: NestedMap[Quantity],
- results: NestedMap[Quantity],
- func_name: str = 'f',
- simplify_units: bool = True,
Symbolic Functions¶
The following functions redefine mathematical functions on the symbolic quantities.
jacobian¶
qsum¶
- simu.qsum(
- quantity: Quantity,
Sum a symbol vector quantity same was a
casadi.sum1, considering units of measurements. This function only applies to quantity objects withcasadi.SXobjects as magnitudes.Note
This function sums the elements of the single, but vectorial argument, and is in that different from the builtin
sumfunction that sums over an iterator of objects.
log¶
- simu.log(quantity: _V) _V¶
Determine natural logarithms, considering units of measurements. The main intent is to use this version for symbolic quantities and QuantityDict objects, but it also works on floats.
>>> x = Quantity(10.0, "cm/m") >>> log(x) <Quantity(-2.30258509, 'dimensionless')>
>>> a = {"A": SymbolQuantity("A", "dimless"), ... "B": SymbolQuantity("B", "dimless")} >>> y = log(a) >>> for key, value in y.items(): print(f"{key}: {value:~}") A: log(A) B: log(B)
>>> log(10) <Quantity(2.30258509, 'dimensionless')>
The other unary functions are defined in the same manner.
sqrt¶
- simu.sqrt(quantity: _V) _V¶
The square root function is a special case of a unary function in that the argument is not required to be dimensionless.
>>> a = QuantityDict({ ... "B": Quantity("1 m**2"), ... "C": Quantity("2500 cm**2")}) >>> print(sqrt(a)) {'B': <Quantity(1.0, 'meter')>, 'C': <Quantity(50.0, 'centimeter')>}
qpow¶
conditional¶
- simu.conditional( ) Quantity¶
Element-wise branching into the positive and negative branch depending on the condition and considering the units of measurements.
>>> x = SymbolQuantity("x", "m") >>> y = conditional(x > 0, -x, x) # abs function >>> print(y) @1=0, @2=((@1<x)==@1), ((@2?(-x):0)+((!@2)?x:0)) meter
As non-recommended as it is to use this function exessively, the resulting
casadiexpression is indeed overcomplicated and could simplify to(x>0)?(x):(-x) meter
Note
You cannot just code
x if x > 0 else -x, as this would not allowcasadibranching dynamically dependent on the value ofxlater-on.
Utility functions¶
qvertcat¶
base_unit¶
base_magnitude¶
- simu.base_magnitude(
- quantity: Quantity,
Return the magnitude of the quantity in base units. This works for symbolic and numeric quantities, and for scalars and vectors
>>> base_magnitude(Quantity("1 km")) 1000.0 >>> base_magnitude(Quantity("20 degC")) 293.15 >>> int(base_magnitude(Quantity("1 barg"))) 201325 >>> base_magnitude(Quantity("speed_of_light")) 299792458.0
The base units are likely the SI unit system, but code shall not rely on this fact - only that it is a cosistent (and offset-free) unit system.
Note
“Use SI or I will BTU you with my feet!”
I saw this sentence once on the T-shirt of a nerd. It turns out it was a mirror.
flatten_dictionary¶
- simu.flatten_dictionary( ) Mapping[str, _V]¶
Convert the given structure into a flat list of key value pairs, where the keys are
SEPARATOR-separated concatonations of the paths, and values are the values of the leafs. Non-string keys are converted to strings. Occurances ofSEPARATORare escaped by\.>>> d: NestedMap[int] = {"a": {"b": 1, "c": 2}, "d": {"e/f": 3}} >>> flatten_dictionary(d) {'a/b': 1, 'a/c': 2, 'd/e\\/f': 3}
unflatten_dictionary¶
- simu.unflatten_dictionary( ) NestedMap[_V]¶
This is the reverse of
flatten_dictionary(), inflating the given one-depth dictionary into a nested structure.>>> d = {"a/b": 1, "a/c": 2, r"d/e\/f": 3} >>> unflatten_dictionary(d) {'a': {'b': 1, 'c': 2}, 'd': {'e/f': 3}}
extract_units_dictionary¶
simplify_quantity¶
- simu.simplify_quantity(
- quantity: Quantity,
Try to convert the unit into a more compact notation, allowing derived units, such as Watt, Joule and Pascal to be used.
Examples:
>>> q = Quantity(1.0, "kg * m**2 / mol / s**2") >>> print(f"{simplify_quantity(q):~}") 1.0 J / mol
>>> q = Quantity(1.0, "kg / K / s**3") >>> print(f"{simplify_quantity(q):~}") 1.0 W / K / m ** 2
>>> q = Quantity(1.0, "kg / m**2 / s**2") >>> print(f"{simplify_quantity(q):~}") 1.0 Pa / m
>>> q = Quantity(1.0, "kg * m**2 / s**3 / A**2") >>> print(f"{simplify_quantity(q):~}") 1.0 Ω
>>> q = Quantity(1.0, "m**3 / s") >>> print(f"{simplify_quantity(q):~}") 1.0 m ** 3 / s
parse_quantities_in_struct¶
- simu.parse_quantities_in_struct( ) Quantity | NestedMap[Quantity]¶
Return a new struct that contains parsed quantities at the leaf values of the given input structure.
The structure can be a nested dictionary, given the keys as strings and the leaf values as strings that can be parsed as
pintquantities. For example:>>> from pprint import pprint >>> y = parse_quantities_in_struct({ ... 'speed': { ... 'car': '100 km/hr', ... 'snail': '1 cm/min', ... 'fingernail': '1.2 mm/day'}, ... 'weight': { ... 'car': '1.5 t', ... 'snail': '10 g', ... 'fingernail': '300 mg'} ... }) >>> pprint(y) {'speed': {'car': <Quantity(100.0, 'kilometer / hour')>, 'fingernail': <Quantity(1.2, 'millimeter / day')>, 'snail': <Quantity(1.0, 'centimeter / minute')>}, 'weight': {'car': <Quantity(1.5, 'metric_ton')>, 'fingernail': <Quantity(300, 'milligram')>, 'snail': <Quantity(10, 'gram')>}}
quantity_dict_to_strings¶
- simu.quantity_dict_to_strings( ) str | NestedMap[str]¶
Return a new structure with the quantity instances replaced by a string representation that is parsable by the
simu.Quantityconstructor.Example:
>>> from pprint import pprint >>> from simu import Quantity >>> struct = {'speed': {'car': Quantity(400 / 3, 'kilometer / hour'), ... 'fingernail': Quantity(1.2, 'millimeter / day'), ... 'snail': Quantity(1.0, 'centimeter / minute')}, ... 'weight': {'car': Quantity(1.5, 'metric_ton'), ... 'fingernail': Quantity(300, 'milligram'), ... 'snail': Quantity(10, 'gram')}} >>> pprint(quantity_dict_to_strings(struct)) {'speed': {'car': '133.33333333333334 km / h', 'fingernail': '1.2 mm / d', 'snail': '1 cm / min'}, 'weight': {'car': '1.5 t', 'fingernail': '300 mg', 'snail': '10 g'}}
extract_sub_structure¶
- simu.extract_sub_structure( ) NestedMap[Quantity]¶
Given a nested structure map
structurethat defines the units of measurement of leaf value quantities, extract those quantities from a source structuresource. AKeyErroris raised if the source structure does not contain the requested data, and aDimensionalityErroris raised if the queried quantity is of incompatible dimensions.Example:
>>> from simu import Quantity >>> src = {"a": {"b": Quantity(1, "km")}, ... "c": Quantity(3, "degC"), ... "d": {"e": Quantity(2, "s"), "f": Quantity(3, "kJ")}} >>> struct = {"a": {"b": "m"}, "d": {"e": "s"}} >>> print(extract_sub_structure(src, struct)) {'a': {'b': <Quantity(1, 'kilometer')>}, 'd': {'e': <Quantity(2, 'second')>}}