Standard format for quantity in derived dimension in `pint`

Viewed 235

TLDR: I'd like to have pint quantities, that are in a certain (derived) dimension, to be converted into a pre-set unit by default.


Details:

I deal with 5 dimensions, as specified below. Note that [power] (usually in MW) and [price] (usually in Eur/MWh) are the derived dimensions.

# units.txt

# Base dimensions and their units
hour = [time] = h = hr
megawatthour = [energy] = MWh
euro = [currency] = Eur = €

# Derived dimensions and their units
[power] = [energy] / [time]
megawatt = megawatthour / hour = MW
[price] = [currency] / [energy]
euro_per_MWh = euro / megawatthour  = Eur/MWh

My question: is it possible to specify that calculated quantities with dimension [power] are by default to be converted to MW?


Here's an example:

import pint

ureg = pint.UnitRegistry("units.txt",)
ureg.default_format = ".0f~P"

energy = 100 * ureg.MWh
time = 4 * ureg.h
revenue = 4000 * ureg.euro

price = revenue / energy
power = energy / time

print(energy, time, revenue, price, power) 
# 100 MWh 4 h 4000 Eur 40 Eur/MWh 25 MWh/h

Here, the power is expressed in MWh/h, because of the way it is calculated, and I can 'convert' it to MW by calling power.to('MW'). Is it possible to automatically do this every time a quantity in this dimension is calculated?

Note that doing a blanket .to_base_units() on all quantities reverts it back to MWh/h.

Remarks:

  • I don't want to use [power] as a base dimension. It moves the problem to the price anyway.
  • I'm aware of prefixes; I just left them out here to keep the example as short as possible.
1 Answers

To my knowledge Pint doesn't currently support setting desired conversion preferences. There has been some chatter about this on the project repo and this issue links to a string of related issues.

What you want can be achieved with some custom code with the caveat that the output needs to be wrapped in a function.

# units-2.txt

# Base dimensions and their units
hour = [time] = h = hr
megawatt = [power] = MW 
euro = [currency] = Eur = €

# Derived dimensions and their units
[energy] = [power] * [time]
megawatthour = megawatt * hour = MWh
[price] = [currency] / [energy]
euro_per_MWh = euro / megawatthour  = Eur/MWh
import pint

preference = ["Eur/MWh", 'MW']

def beautify(x, preference=preference):
    for preferred_unit in preference:
        try:
            return x.to(preferred_unit)
        except pint.DimensionalityError:
            pass
    # if no preferred unit fits, leave it as it is
    return x

def test_defs(file):
    print(file)
    ureg = pint.UnitRegistry(file,)
    ureg.default_format = ".0f~P"
    energy = 100 * ureg.MWh
    time = 4 * ureg.h
    revenue = 4000 * ureg.euro
    price = revenue / energy
    power = energy / time
    print(time.to_base_units())
    print(f"beautified time: {beautify(time)}")  # Units with no preference remain the same.
    print(power.to_base_units())
    print(f"beautified power: {beautify(power)}")  # Units with preference can be changed.
    print(price.to_base_units())
    print(f"beautified price: {beautify(price)}")  # Units with preference can be changed.

test_defs("units.txt")
test_defs("units-2.txt")

This will print

units.txt
4 h
beautified time: 4 h
25 MWh/h
beautified power: 25 MW
40 Eur/MWh
beautified price: 40 Eur/MWh
units-2.txt
4 h
beautified time: 4 h
25 MW
beautified power: 25 MW
40 Eur/MW/h
beautified price: 40 Eur/MWh
Related