Generality of context transformations in python pint package

Viewed 22

I am working on a project involving natural gas and want to be able to convert between volume, mass and energy using provided densities. I have the following code python, using the pint package:

import pint
ureg = pint.UnitRegistry()

ureg.define('Sm3 = m^3')  # cubic meter of gas at given temperature and pressure 
NG_DENS = 0.8 * ureg('kg/Sm3')                # density
NG_SE = 50 * ureg('MJ/kg')                    # specific energy (energy per mass)
NG_ED = (NG_SE * NG_DENS).to_reduced_units()  # energy density (energy per volume)

ng = pint.Context('ngas')
ng.add_transformation('[volume]', '[mass]', lambda ureg, x: x * NG_DENS)
ng.add_transformation('[mass]', '[energy]', lambda ureg, x: x * NG_SE)
ureg.add_context(ng)

print(f"{ureg('1 Sm3').to('kg', 'ngas') = }")
print(f"{ureg('1 Sm3').to('MJ', 'ngas') = }")
print(f"{ureg('1 Sm3/s').to('MJ/s', 'ngas') = }")
print(f"{ureg('1 Sm3/s').to('MW', 'ngas') = }")

The first two print lines work, showing that the transformations are transitive (we define conversions from volume to mass and mass to energy, so it is also possible to transform volume to energy). Hence, I do not actually need NG_ED.

However, for the last two lines to work I have to add one of the following transformations to the definition of context ng:

ng.add_transformation('[volume]/[time]', '[energy]/[time]', lambda ureg, x: x * NG_ED)
ng.add_transformation('[volume]/[time]', '[power]', lambda ureg, x: x * NG_ED)

And if I wanted to convert [mass]/[time] to [power], I would have to add that transformation as well.

Why doesn't pint figure this out? And, in general, what kind of relationships can I expect pint to deduce self and what has to be specified?

Or am I using the library wrong, i.e., is there a better way to achieve these transformations?

I am using pint 0.19.2 on python 3.9.1 on Windows.

0 Answers
Related