Reputation: 4564
The following code uses pint to convert a mass flow to a volumetric flow. To convert from mass to volume, the density of the gas must be calculated. The calculation returns the correct values in the desired units.
import pint
ureg = pint.UnitRegistry()
Q_ = ureg.Quantity
def density(T):
R = 287 * ureg('J/kg/K')
P = 101325 * ureg('Pa')
return (P / R / T.to(ureg.kelvin)).to(ureg('kg/m^3'))
@ureg.wraps(ret='m^3/sec', args=['kg/sec', 'kg/m^3'])
def volumetric_flow_rate(mass_flow_rate, rho):
return mass_flow_rate / rho
mfr = 1 * ureg('kg/s')
temperature = Q_(25, ureg.degC)
rho = density(temperature)
print(rho)
# 1.1841314120000166 kilogram / meter ** 3
v = volumetric_flow_rate(mfr, rho)
print(v)
# 0.8445008635578583 meter ** 3 / second
However, when I apply to use the @ureg.wraps
decorator on the density
function in the same way used for volumetric_flow_rate
, the value is correct, but the units are not the desired units.
@ureg.wraps(ret='kg/m^3', args=['K'])
def density(T):
R = 287 * ureg('J/kg/K')
P = 101325 * ureg('Pa')
return P / R / T
rho = density(temperature)
print(rho)
# 1.1841314120000166 kelvin * kilogram * pascal / joule kilogram / meter ** 3
v = volumetric_flow_rate(mfr, rho)
print(v)
# 0.8445008635578583 joule / kelvin / kilogram / pascal meter ** 3 / second
The following versions of the decorator all yield the same results (incorrect return units) for the density
function:
@ureg.wraps(ret='kg/m^3', args='K')
@ureg.wraps(ret='kg/m^3', args=['K'])
@ureg.wraps(ret='kg/m^3', args=ureg.kelvin)
@ureg.wraps(ret='kg/m^3', args='K', strict=True)
@ureg.wraps(ret='kg/m^3', args=['K'], strict=True)
@ureg.wraps(ret='kg/m^3', args=ureg.kelvin, strict=True)
@ureg.wraps(ret='kg/m^3', args='K', strict=False)
@ureg.wraps(ret='kg/m^3', args=['K'], strict=False)
@ureg.wraps(ret='kg/m^3', args=ureg.kelvin, strict=False)
The following version of the decorator raises an error
@ureg.wraps(ret=ureg('kg/m^3'), args=ureg.kelvin)
TypeError: wraps 'ret' argument must by of type str or Unit, not <class 'pint.quantity.build_quantity_class.<locals>.Quantity'> (1.0 kilogram / meter ** 3)
With that error message, I defined a custom unit mass_flow
ureg.define('mass_flow = 1 * kg / m^3')
print(ureg.mass_flow)
# mass_flow
print(type(ureg.mass_flow))
# <class 'pint.unit.build_unit_class.<locals>.Unit'>
print(1 * ureg.mass_flow)
# 1 mass_flow
The following decorators don't raise an error, however they still yiled the incorrect units (though now with the mass_flow
"units".
@ureg.wraps(ret=ureg.mass_flow, args='K')
@ureg.wraps(ret='mass_flow', args='K')
# 1.1841314120000166 kelvin * kilogram * pascal / joule mass_flow
One last ditch attempt I tried was to define R
and P
outside of the function. Didn't expect it to change the answer, and expectations were correct.
R = 287 * ureg('J/kg/K')
P = 101325 * ureg('Pa')
@ureg.wraps(ret='kg/m^3', args='K')
def density(T):
return P / R / T
How can I define the decorator for the density
function in a similar manner to the volumetric_flow_rate
function so that the body of the function is cleaner and does not require me to do the internal unit conversions?
Furthermore, if the volumetric_flow_rate
function has a complex return unit m^3/sec
why would kg/m^3
not work for the density
function?
Python 3.7.0, Pint 0.10.1
Upvotes: 1
Views: 119
Reputation: 4564
Pint appears to strip away all units before entering the function and then applies units when the function is over. As such, either of the following works.
@ureg.wraps(ret='kg/m^3', args=['K', 'Pa', 'J/kg/K'])
def density(T, P=101325 * ureg.Pa, R=287 * ureg('J/kg/K')):
return P / R / T
@ureg.wraps(ret='kg/m^3', args=['K', 'Pa', 'J/kg/K'], strict=False)
def density(T, P=101325, R=287):
return P / R / T
The key is to ensure everything in the function is dimensionless. If you call another function within a @ureg.wraps
decorated function, and that function returns a dimensional quantity, convert it to the required units and then strip the units by using the .magnitude
attribute.
Upvotes: 1