Skip to content

Commit 08e26e9

Browse files
Flix6xnhoening
andauthored
Fix/units currencies with SI prefixes (#1844)
* style: fix spaces in JSON in reporter tutorial Signed-off-by: F.N. Claessen <felix@seita.nl> * fix: handle SI-prefixed currencies such as kEUR Signed-off-by: F.N. Claessen <felix@seita.nl> * docs: changelog entry Signed-off-by: F.N. Claessen <felix@seita.nl> --------- Signed-off-by: F.N. Claessen <felix@seita.nl> Co-authored-by: Nicolas Höning <nicolas@seita.nl>
1 parent 1613672 commit 08e26e9

File tree

3 files changed

+47
-25
lines changed

3 files changed

+47
-25
lines changed

documentation/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Bugfixes
6767
* Allow consultants to view client users [see `PR #1755 <https://www.github.com/FlexMeasures/flexmeasures/pull/1755>`_]
6868
* Fix bug where removed flex-model fields don't show up under the dropdown for new fields, except after a page refresh [see `PR #1775 <https://www.github.com/FlexMeasures/flexmeasures/pull/1775>`_]
6969
* Fix bug in displaying user audit log (incl. expanding its API schema) [see `PR #1779 <https://github.com/FlexMeasures/flexmeasures/pull/1779>`_]
70+
* Support flex-context prices with SI-prefixed currencies (e.g. kEUR) [see `PR #1844 <https://github.com/FlexMeasures/flexmeasures/pull/1844>`_]
7071

7172

7273
v0.29.0 | October 14, 2025

documentation/tut/toy-example-reporter.rst

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ In practice, we need to create the `config` and `parameters`:
108108
109109
$ echo "
110110
$ {
111-
$ 'weights' : {
112-
$ 'grid connection capacity' : 1.0,
113-
$ 'PV' : -1.0,
111+
$ 'weights': {
112+
$ 'grid connection capacity': 1.0,
113+
$ 'PV': -1.0,
114114
$ }
115115
$ }" > headroom-config.json
116116
@@ -119,9 +119,9 @@ In practice, we need to create the `config` and `parameters`:
119119
120120
$ echo "
121121
$ {
122-
$ 'input' : [{'name' : 'grid connection capacity','sensor' : 7},
123-
$ {'name' : 'PV', 'sensor' : 3}],
124-
$ 'output' : [{'sensor' : 8}]
122+
$ 'input': [{'name': 'grid connection capacity', 'sensor': 7},
123+
$ {'name': 'PV', 'sensor': 3}],
124+
$ 'output': [{'sensor': 8}]
125125
$ }" > headroom-parameters.json
126126
127127
The output sensor (ID: 8) is actually the one created just to store that information - the headroom our battery has when considering solar production.
@@ -177,8 +177,8 @@ Define parameters in a JSON file:
177177
178178
$ echo "
179179
$ {
180-
$ 'input' : [{'sensor' : 4}],
181-
$ 'output' : [{'sensor' : 9}]
180+
$ 'input': [{'sensor': 4}],
181+
$ 'output': [{'sensor': 9}]
182182
$ }" > inflexible-parameters.json
183183
184184
Create report:
@@ -205,8 +205,8 @@ Define parameters in a JSON file:
205205
206206
$ echo "
207207
$ {
208-
$ 'input' : [{'sensor' : 5}],
209-
$ 'output' : [{'sensor' : 10}]
208+
$ 'input': [{'sensor': 5}],
209+
$ 'output': [{'sensor': 10}]
210210
$ }" > breakable-parameters.json
211211
212212
Create report:
@@ -233,8 +233,8 @@ Define parameters in a JSON file:
233233
234234
$ echo "
235235
$ {
236-
$ 'input' : [{'sensor' : 6}],
237-
$ 'output' : [{'sensor' : 11}]
236+
$ 'input': [{'sensor': 6}],
237+
$ 'output': [{'sensor': 11}]
238238
$ }" > shiftable-parameters.json
239239
240240
Create report:

flexmeasures/utils/unit_utils.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
PREFERRED_UNITS_DICT = dict(
5757
[(ur.parse_expression(x).dimensionality, x) for x in PREFERRED_UNITS]
5858
)
59+
SI_PREFIXES = list(ur._prefixes.keys())
5960

6061

6162
def to_preferred(x: pint.Quantity) -> pint.Quantity:
@@ -269,56 +270,76 @@ def is_currency_unit(unit: str | pint.Quantity | pint.Unit) -> bool:
269270
return Currency(code=unit) in list_all_currencies()
270271

271272

273+
def strip_si_prefix(unit: str) -> str:
274+
"""For example:
275+
>>> strip_si_prefix("MEUR")
276+
'EUR'
277+
>>> strip_si_prefix("kEUR")
278+
'EUR'
279+
>>> strip_si_prefix("cEUR")
280+
'EUR'
281+
"""
282+
if len(unit) == 4 and unit[0] in SI_PREFIXES:
283+
return unit[1:]
284+
return unit
285+
286+
272287
def is_price_unit(unit: str) -> bool:
273288
"""For example:
274289
>>> is_price_unit("EUR/MWh")
275290
True
291+
>>> is_price_unit("kEUR/MWh")
292+
True
276293
>>> is_price_unit("KRW/MWh")
277294
True
278295
>>> is_price_unit("KRW/MW")
279296
True
280297
>>> is_price_unit("beans/MW")
281298
False
282299
"""
283-
if (
284-
unit[:3] in [str(c) for c in list_all_currencies()]
285-
and len(unit) > 3
286-
and unit[3] == "/"
287-
):
288-
return True
289-
return False
300+
if "/" not in unit:
301+
return False
302+
currency, _ = unit.split("/", 1)
303+
currency = strip_si_prefix(currency)
304+
return currency in [str(c) for c in list_all_currencies()]
290305

291306

292307
def is_energy_price_unit(unit: str) -> bool:
293308
"""For example:
294309
>>> is_energy_price_unit("EUR/MWh")
295310
True
311+
>>> is_energy_price_unit("kEUR/MWh")
312+
True
296313
>>> is_energy_price_unit("KRW/MWh")
297314
True
298315
>>> is_energy_price_unit("KRW/MW")
299316
False
300317
>>> is_energy_price_unit("beans/MW")
301318
False
302319
"""
303-
if is_price_unit(unit) and is_energy_unit(unit[4:]):
304-
return True
305-
return False
320+
if not is_price_unit(unit):
321+
return False
322+
denom = unit.split("/", 1)[1]
323+
return is_energy_unit(denom)
306324

307325

308326
def is_capacity_price_unit(unit: str) -> bool:
309327
"""For example:
310328
>>> is_capacity_price_unit("EUR/MW")
311329
True
330+
>>> is_capacity_price_unit("kEUR/MW")
331+
True
312332
>>> is_capacity_price_unit("KRW/MW")
313333
True
314334
>>> is_capacity_price_unit("KRW/MWh")
315335
False
316336
>>> is_capacity_price_unit("beans/MWh")
317337
False
318338
"""
319-
if is_price_unit(unit) and is_power_unit(unit[4:]):
320-
return True
321-
return False
339+
if not is_price_unit(unit):
340+
return False
341+
denom = unit.split("/", 1)[1]
342+
return is_power_unit(denom)
322343

323344

324345
def is_speed_unit(unit: str) -> bool:

0 commit comments

Comments
 (0)