menu

Questions & Answers

Difficulty with custom conversion factor using contexts with pint

I am looking to set up a fixed conversion factor between two specific units that cause a DimensionalityError in pint.

I have daily accumulated solar radiation data that is in J/m2 and want to convert it into W/m2. Since this is a daily accumulation, we just need to multiply by (1/86400). (See, e.g. https://confluence.ecmwf.int/display/CKB/ERA5%3A+data+documentation#ERA5:datadocumentation-Monthlymeans).

When trying to convert with pint, it understandably gives an error.

import pint
ureg = pint.UnitRegistry()
q = 1 * ureg.Unit('joule / meter ** 2')
q.to('watt / meter ** 2')
DimensionalityError: Cannot convert from 'joule / meter ** 2' ([mass] / [time] ** 2) to 'watt / meter ** 2' ([mass] / [time] ** 3)

I am trying to create a custom context to deal with this, but I'm still getting the same error.

# Arbitrary name for the context following the tutorial
c = pint.Context('ab')

# Add transformation between dimensionality of J/m2 to dimensionality of W/m2
c.add_transformation(
    '[mass] / [time] ** 2',
    '[mass] / [time] ** 3',
    lambda ureg, x: x * (1/86400)
)
ureg.add_context(c)
ureg("1 joule / meter ** 2").to("watt / meter ** 2", 'ab')
DimensionalityError: Cannot convert from 'joule / meter ** 2' ([mass] / [time] ** 2) to 'watt / meter ** 2' ([mass] / [time] ** 3)

I've also tried to re-define them as custom units with conversions and it doesn't work either.

# Trying to be extremely explicit with these unit names.
ureg.define('solar_irradiance_joules = 86400 * solar_irradiance_watts = joule / meter ** 2 = _') ureg.define('solar_irradiance_watts = (1/86400) * solar_irradiance_joules = watt / meter ** 2 = _')

q = ureg.Quantity(1, 'solar_irradiance_joules')
q.to('solar_irradiance_watts')
RecursionError: maximum recursion depth exceeded while calling a Python object

Or more simply...

ureg.define('watt / meter ** 2 = 86400 * joule / meter ** 2')
q = ureg.Quantity(1, 'joule / meter ** 2')
q.to('watt / meter ** 2')
DimensionalityError: Cannot convert from 'joule / meter ** 2' ([mass] / [time] ** 2) to 'watt / meter ** 2' ([mass] / [time] ** 3)```
Comments:
2023-01-21 00:30:07
Your units don't make sense to me. If you have a daily accumulation, your units should be J per m² per day.
2023-01-21 00:30:07
Thanks, this actually helped me arrive at the solution. It seems like the proper label for the first units are J per m² per day, so that the following works as expected: q = 1 * ureg.Unit('joule / meter ** 2 / day'); q.to('watt / meter ** 2'). Since the W/m2 implicitly integrates across time here. I just need to force-append the /day part of the units to the observations that are ingested.
Answers(1) :

@MatBailie pointed out some unit errors in the way I was thinking here.

This works with the following:

import pint
ureg = pint.UnitRegistry()
q = 1 * ureg.Unit('joule / meter ** 2 / day')
q.to('watt / meter ** 2')