Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pythonically tuple magnitude and physical units?

Tags:

python

tuples

Now that the question of how to parse a string for magnitude and physical unit is settled, the next question is, how should one best go on about packing these two together in a way that on the one hand does not cost too much performance but on the other hand adds a unit validation.

To make clear what I mean, take as an example two velocities v = 5 m/s and u = 10 mph. The previous question already takes care of converting everything to SI units (so you won't crash another Mars mission due to that). So internally one would have e.g. a tuple v = (5, m/s) and u = (4.4704, m/s) and the output routine would take care of using the preferred units for output. While applying unit-compatible operations on the two, e.g. addition, or subtracting their squares, are valid, others like v - 1/u are complete and utter nonsense. But how should this best be implemented? Some possibilities that I considered so far:

  • Don't. Since internally only SI units are used it might be obvious which units apply. But neglecting that information is very likely to become a source of bugs if the formulae become more complex
  • Subclass tuple and override all valid operations prepending unit consistency checks. Sounds fun...
  • Store the values as sympy.core.mul.Muls of the magnitude times a (meaningful function of) sympy.physics.unit.Unit, e.g v = 5*unit.m/unit.s. I don't expect great performance of this, plus I'd still have to check whether the result of an operation is still of the form magnitude * unit.
  • Use a numpy.array with an additional sympy.physics.unit.Unit entry, which already implements the elementwise operations. This would still require a manual unit consistency check afterwards (the fact that e.g. m+m=2m would also need to be treated...)
  • Subclass numpy.array instead of tuple, override the operators prepending the unit consistency check before calling the super-operators on the array without the unit. Maybe some __getattribute__ magic could simplify this since all valid operations are already implemented for both magnitude and unit...

Is one of these solutions good/pythonic? Or what other way is there? Does a library that treats this already exist?

edit Note that this should not be restricted to scalar values; vectors, matrices (maybe even sympy.Symbols) should also work

like image 949
Tobias Kienzler Avatar asked Nov 13 '22 07:11

Tobias Kienzler


1 Answers

Use the Magnitude library:

from magnitude import mg

m = mg(5, 'kg')
a = mg(9.82, 'm/s2')
f = m * a
print f, f == mg(49.1, 'N')

u = mg(70, 'km/h')
g = mg(9.82, 'm/s2')
s = (u**2) / (2*g)
print s, s > mg(10, 'm')

The interesting thing happens when there is a unit mismatch:

>>> m = mg(5, 'kg')
>>> a = mg(9.82, 'm/s')
>>> f = m * a
>>> print f, f == mg(49.1, 'N')
MagnitudeError: Incompatible units in comparison: [1, -2, 0, 1, 0, 0, 0, 0, 0] and [1, -1, 0, 1, 0, 0, 0, 0, 0]

Not the best error message perhaps, but it effectively prevents you from handling values in the wrong way.

like image 154
Björn Lindqvist Avatar answered Nov 14 '22 21:11

Björn Lindqvist