Skip to content

Commit

Permalink
Upgrade pint==0.19.1 (#415)
Browse files Browse the repository at this point in the history
Upgrade pint, which now uses h to denote hours instead of Planck's constant (making my workaround redundant). Also prefer shorter units as a general rule.


* Upgrade pint to 0.19 in which h is the symbol for hour rather than Planck's constant

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Simplify setup of unit registry

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Add currencies to list of preferred units

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Always prefer shorter units

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Add todo to take into account magnitude when deciding what the shortest unit is (requires decimal registry)

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Add todo to switch to decimal unit registry

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Add module docstring

Signed-off-by: F.N. Claessen <felix@seita.nl>

* Changelog entry

Signed-off-by: F.N. Claessen <felix@seita.nl>
  • Loading branch information
Flix6x committed Apr 9, 2022
1 parent 64c6ff5 commit 559eccd
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 30 deletions.
1 change: 1 addition & 0 deletions documentation/changelog.rst
Expand Up @@ -15,6 +15,7 @@ Bugfixes

Infrastructure / Support
----------------------
* Unit conversion prefers shorter units in general [see `PR #415 <http://www.github.com/FlexMeasures/flexmeasures/pull/415>`_]


v0.9.2 | April XX, 2022
Expand Down
4 changes: 3 additions & 1 deletion flexmeasures/utils/tests/test_unit_utils.py
Expand Up @@ -70,7 +70,7 @@ def test_convert_unit(
("m³", None, "m³/h"),
("kWh", None, "kW"),
("km", "h", "km/h"),
("m", "s", "km/h"),
("m", "s", "m/s"),
],
)
def test_determine_flow_unit(
Expand All @@ -88,6 +88,8 @@ def test_determine_flow_unit(
"unit, time_unit, expected_unit",
[
("m³/h", None, "m³"),
("km³/h", None, "km³"),
# ("hm³/h", None, "hm³"), # todo: uncomment after switching to decimal unit registry
("kW", None, "kWh"),
("m/s", "s", "m"),
("m/s", "h", "km"),
Expand Down
54 changes: 27 additions & 27 deletions flexmeasures/utils/unit_utils.py
@@ -1,44 +1,36 @@
"""Utility module for unit conversion
FlexMeasures stores units as strings in short scientific notation (such as 'kWh' to denote kilowatt-hour).
We use the pint library to convert data between compatible units (such as 'm/s' to 'km/h').
Three-letter currency codes (such as 'KRW' to denote South Korean Won) are valid units.
Note that converting between currencies requires setting up a sensor that registers conversion rates over time.
The preferred compact form for combinations of units can be derived automatically (such as 'kW*EUR/MWh' to 'EUR/h').
Time series with fixed resolution can be converted from units of flow to units of stock (such as 'kW' to 'kWh'), and vice versa.
Percentages can be converted to units of some physical capacity if a capacity is known (such as '%' to 'kWh').
"""

from datetime import timedelta
from typing import List, Optional, Union

from moneyed import list_all_currencies
import importlib.resources as pkg_resources
import numpy as np
import pandas as pd
import pint
import timely_beliefs as tb

# Edit constants template to stop using h to represent planck_constant
constants_template = (
pkg_resources.read_text(pint, "constants_en.txt")
.replace("= h ", " ")
.replace(" h ", " planck_constant ")
)

# Edit units template to use h to represent hour instead of planck_constant
units_template = (
pkg_resources.read_text(pint, "default_en.txt")
.replace("@import constants_en.txt", "")
.replace(" h ", " planck_constant ")
.replace("hour = 60 * minute = hr", "hour = 60 * minute = h = hr")
)

# Create custom template
custom_template = [f"{c} = [currency_{c}]" for c in list_all_currencies()]

# Join templates as iterable object
full_template = (
constants_template.split("\n") + units_template.split("\n") + custom_template
)

# Set up UnitRegistry with abbreviated scientific format
ur = pint.UnitRegistry(
full_template,
# non_int_type=decimal.Decimal, # todo: switch to decimal unit registry, after https://github.com/hgrecco/pint/issues/1505
preprocessors=[
lambda s: s.replace("%", " percent "),
lambda s: s.replace("‰", " permille "),
],
)
ur.load_definitions(custom_template)
ur.default_format = "~P" # short pretty
ur.define("percent = 1 / 100 = %")
ur.define("permille = 1 / 1000 = ‰")
Expand All @@ -57,6 +49,8 @@
"V",
"A",
"dimensionless",
] + [
str(c) for c in list_all_currencies()
] # todo: move to config setting, with these as a default (NB prefixes do not matter here, this is about SI base units, so km/h is equivalent to m/h)
PREFERRED_UNITS_DICT = dict(
[(ur.parse_expression(x).dimensionality, x) for x in PREFERRED_UNITS]
Expand All @@ -67,7 +61,16 @@ def to_preferred(x: pint.Quantity) -> pint.Quantity:
"""From https://github.com/hgrecco/pint/issues/676#issuecomment-689157693"""
dim = x.dimensionality
if dim in PREFERRED_UNITS_DICT:
return x.to(PREFERRED_UNITS_DICT[dim]).to_compact()

compact_unit = x.to(PREFERRED_UNITS_DICT[dim]).to_compact()

# todo: switch to decimal unit registry and then swap out the if statements below
# if len(f"{compact_unit.magnitude}" + "{:~P}".format(compact_unit.units)) < len(
# f"{x.magnitude}" + "{:~P}".format(x.units)
# ):
# return compact_unit
if len("{:~P}".format(compact_unit.units)) < len("{:~P}".format(x.units)):
return compact_unit
return x


Expand Down Expand Up @@ -126,11 +129,8 @@ def determine_stock_unit(flow_unit: str, time_unit: str = "h"):
>>> determine_stock_unit("m³/h") # m³
>>> determine_stock_unit("kW") # kWh
"""
stock = ur.Quantity(flow_unit) * ur.Quantity(time_unit)
return min(
["{:~P}".format(stock.units), "{:~P}".format(to_preferred(stock).units)],
key=len,
)
stock = to_preferred(ur.Quantity(flow_unit) * ur.Quantity(time_unit))
return "{:~P}".format(stock.units)


def units_are_convertible(
Expand Down
2 changes: 1 addition & 1 deletion requirements/app.in
Expand Up @@ -6,7 +6,7 @@ pscript
pandas
# pandas-bokeh 0.5 requires bokeh>=2.0, but bokeh still doesn't support sharing a legend across plots
pandas-bokeh==0.4.3
pint
pint>=0.19.1
py-moneyed
iso8601
xlrd
Expand Down
2 changes: 1 addition & 1 deletion requirements/app.txt
Expand Up @@ -241,7 +241,7 @@ pillow==9.0.1
# via
# bokeh
# matplotlib
pint==0.18
pint==0.19.1
# via -r requirements/app.in
ply==3.11
# via pyomo
Expand Down

0 comments on commit 559eccd

Please sign in to comment.