zen-garden 2.8.13__py3-none-any.whl → 2.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zen_garden/__init__.py +1 -0
- zen_garden/cli/zen_garden_cli.py +21 -13
- zen_garden/cli/zen_operation_cli.py +89 -0
- zen_garden/default_config.py +0 -3
- zen_garden/optimization_setup.py +40 -176
- zen_garden/postprocess/postprocess.py +4 -34
- zen_garden/postprocess/results/solution_loader.py +4 -0
- zen_garden/preprocess/unit_handling.py +393 -100
- zen_garden/runner.py +28 -34
- zen_garden/wrapper/__init__.py +0 -0
- zen_garden/wrapper/operation_scenarios.py +248 -0
- zen_garden/wrapper/utils.py +438 -0
- {zen_garden-2.8.13.dist-info → zen_garden-2.9.0.dist-info}/METADATA +2 -2
- {zen_garden-2.8.13.dist-info → zen_garden-2.9.0.dist-info}/RECORD +17 -13
- {zen_garden-2.8.13.dist-info → zen_garden-2.9.0.dist-info}/entry_points.txt +1 -0
- {zen_garden-2.8.13.dist-info → zen_garden-2.9.0.dist-info}/WHEEL +0 -0
- {zen_garden-2.8.13.dist-info → zen_garden-2.9.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -20,14 +20,38 @@ from zen_garden.utils import get_label_position
|
|
|
20
20
|
|
|
21
21
|
class UnitHandling:
|
|
22
22
|
"""
|
|
23
|
-
|
|
23
|
+
A class for managing and converting units in an energy system model.
|
|
24
|
+
|
|
25
|
+
This class facilitates unit consistency checks, dimensionality analysis, and
|
|
26
|
+
unit conversions in energy systems models, particularly those that involve
|
|
27
|
+
energy carriers, technologies, and conversion processes. It helps in
|
|
28
|
+
defining and converting units across various parameters and ensures that
|
|
29
|
+
unit definitions are consistent across the entire system.
|
|
30
|
+
|
|
31
|
+
Key functionalities:
|
|
32
|
+
- Loading and extracting base units for the system.
|
|
33
|
+
- Converting input units into a unified system of base units.
|
|
34
|
+
- Checking for dimensional consistency between input units and
|
|
35
|
+
base units.
|
|
36
|
+
- Redefining and verifying the dimensional matrix of the system.
|
|
37
|
+
- Ensuring that unit conversions and combinations are performed
|
|
38
|
+
accurately.
|
|
24
39
|
"""
|
|
25
40
|
|
|
26
41
|
def __init__(self, folder_path, rounding_decimal_points_units):
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
"""
|
|
43
|
+
Initializes an instance of the UnitHandling class.
|
|
44
|
+
|
|
45
|
+
This constructor processes and stores the system's base unit definitions
|
|
46
|
+
and other configurations. It also defines the rounding tolerance for
|
|
47
|
+
unit conversions.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
folder_path (str or Path): The path to the folder containing system
|
|
51
|
+
specifications (e.g., "unit_definitions.txt", "base_units.csv").
|
|
52
|
+
rounding_decimal_points_units (int): The number of decimal points to
|
|
53
|
+
which units should be rounded during conversion and consistency
|
|
54
|
+
checks.
|
|
31
55
|
"""
|
|
32
56
|
self.folder_path = folder_path
|
|
33
57
|
self.rounding_decimal_points_units = rounding_decimal_points_units
|
|
@@ -37,7 +61,22 @@ class UnitHandling:
|
|
|
37
61
|
self.carrier_energy_quantities = {}
|
|
38
62
|
|
|
39
63
|
def get_base_units(self):
|
|
40
|
-
"""
|
|
64
|
+
"""
|
|
65
|
+
Extracts and initializes the base units of the energy system.
|
|
66
|
+
|
|
67
|
+
This method loads unit definitions, processes them to extract base
|
|
68
|
+
units, and constructs the dimensionality matrix for the system.
|
|
69
|
+
It also checks for duplicates and verifies that no unit can be
|
|
70
|
+
constructed from other base units. Additionally, it ensures that all
|
|
71
|
+
base units have a valid dimensionality and that no linear dependencies
|
|
72
|
+
exist between them.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
KeyError: If there are multiple base units defined for the same
|
|
76
|
+
dimensionality.
|
|
77
|
+
AssertionError: If there are linear dependencies between base units
|
|
78
|
+
that can't be resolved.
|
|
79
|
+
"""
|
|
41
80
|
_list_base_unit = self.extract_base_units()
|
|
42
81
|
self.ureg = UnitRegistry()
|
|
43
82
|
|
|
@@ -98,12 +137,26 @@ class UnitHandling:
|
|
|
98
137
|
dependent_dims = dependent_dims[pos_ones[:, 1], :]
|
|
99
138
|
self.dim_analysis["dependent_dims"] = dependent_dims
|
|
100
139
|
# check that no base unit can be directly constructed from the others (e.g., GJ from GW and hour)
|
|
101
|
-
assert
|
|
140
|
+
assert not UnitHandling.check_pos_neg_boolean(dependent_dims, axis=1), f"At least one of the base units {list(self.base_units.keys())} can be directly constructed from the others"
|
|
102
141
|
|
|
103
142
|
def extract_base_units(self):
|
|
104
|
-
"""
|
|
105
|
-
|
|
106
|
-
|
|
143
|
+
"""
|
|
144
|
+
Extracts the base units from either a CSV or JSON file.
|
|
145
|
+
|
|
146
|
+
If the CSV file (``base_units.csv``) is not found, the method will
|
|
147
|
+
fall back on a JSON file (``base_units.json``) to load the base units.
|
|
148
|
+
If ``hour`` is not found in the list of base units, a warning will
|
|
149
|
+
be raised. This method provides the list of all base units that will be
|
|
150
|
+
used for further calculations and unit consistency checks.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
list:
|
|
154
|
+
A list of base units defined in the system.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
UserWarning: If the hour unit is not found in the base unit
|
|
158
|
+
definitions.
|
|
159
|
+
"""
|
|
107
160
|
if os.path.exists(os.path.join(self.folder_path / "base_units.csv")):
|
|
108
161
|
list_base_units = pd.read_csv(self.folder_path / "base_units.csv").squeeze().values.tolist()
|
|
109
162
|
logging.warning("DeprecationWarning: Specifying the base units in .csv file format is deprecated. Use the .json file format instead.")
|
|
@@ -117,11 +170,30 @@ class UnitHandling:
|
|
|
117
170
|
return list_base_units
|
|
118
171
|
|
|
119
172
|
def calculate_combined_unit(self, input_unit, return_combination=False):
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
173
|
+
"""
|
|
174
|
+
Represents the input unit as a combination of base units.
|
|
175
|
+
|
|
176
|
+
This method constructs a combined unit by converting an input unit into
|
|
177
|
+
a set of base units. It first checks the dimensionality of the input
|
|
178
|
+
unit and constructs the appropriate combined unit through dimensional
|
|
179
|
+
analysis. It then checks for unit consistency with the base units
|
|
180
|
+
and returns the combined unit.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
input_unit (str): The input unit to be converted (e.g., ``kg``, ``m/s``).
|
|
184
|
+
return_combination (bool): If True, also returns the base unit
|
|
185
|
+
combination, in addition to the combined unit.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
pint.Quantity or tuple:
|
|
189
|
+
The combined unit represented as a ``pint.Quantity``. If
|
|
190
|
+
``return_combination=True``, returns a tuple containing the
|
|
191
|
+
combined unit and the base unit combination.
|
|
192
|
+
|
|
193
|
+
Raises:
|
|
194
|
+
AssertionError: If the dimensionality of the input unit cannot be
|
|
195
|
+
matched with base units.
|
|
196
|
+
"""
|
|
125
197
|
# check if "h" and thus "planck_constant" in unit
|
|
126
198
|
self.check_if_invalid_hourstring(input_unit)
|
|
127
199
|
# create dimensionality vector for input_unit
|
|
@@ -223,13 +295,34 @@ class UnitHandling:
|
|
|
223
295
|
|
|
224
296
|
#ToDo: check if combined_unit is described correctly in the header
|
|
225
297
|
def get_unit_multiplier(self, input_unit, attribute_name, path=None, combined_unit=None):
|
|
226
|
-
"""
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
298
|
+
"""
|
|
299
|
+
Calculates the multiplier for converting an input unit into the base
|
|
300
|
+
units.
|
|
301
|
+
|
|
302
|
+
This method computes the scaling factor (multiplier) needed to convert
|
|
303
|
+
the given `input_unit` into a base unit. If the `input_unit` is already
|
|
304
|
+
a base unit, the multiplier is 1. If the `input_unit` is not in base
|
|
305
|
+
units, it computes the conversion using dimensional analysis and ensures
|
|
306
|
+
that the resulting multiplier meets the rounding tolerance.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
input_unit (str): The unit to be converted (e.g., "kg", "m/s").
|
|
310
|
+
attribute_name (str): The name of the attribute that this unit
|
|
311
|
+
corresponds to.
|
|
312
|
+
path (str, optional): The file path associated with the unit
|
|
313
|
+
(for logging purposes).
|
|
314
|
+
combined_unit (pint.Quantity, optional): The combined unit in
|
|
315
|
+
base units. If provided, skips recomputing the combined unit.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
float:
|
|
319
|
+
The multiplier that scales the `input_unit` into the
|
|
320
|
+
base units.
|
|
321
|
+
|
|
322
|
+
Raises:
|
|
323
|
+
AssertionError: If the multiplier is smaller than the rounding
|
|
324
|
+
tolerance.
|
|
325
|
+
"""
|
|
233
326
|
# if input unit is already in base units --> the input unit is base unit, multiplier = 1
|
|
234
327
|
if input_unit in self.base_units:
|
|
235
328
|
return 1
|
|
@@ -254,13 +347,30 @@ class UnitHandling:
|
|
|
254
347
|
return round(multiplier, self.rounding_decimal_points_units)
|
|
255
348
|
|
|
256
349
|
def convert_unit_into_base_units(self, input_unit, get_multiplier=False, attribute_name=None, path=None):
|
|
257
|
-
"""
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
350
|
+
"""
|
|
351
|
+
Converts an input unit into base units
|
|
352
|
+
|
|
353
|
+
This method converts an input unit into the equivalent base units,
|
|
354
|
+
following the dimensional analysis process to express the `input_unit`
|
|
355
|
+
as a combination of base units. Additionally, it can return the
|
|
356
|
+
multiplier that scales the input unit into the base units, depending on
|
|
357
|
+
the value of `get_multiplier`.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
input_unit (str): The unit to be converted (e.g., "kg", "m/s").
|
|
361
|
+
attribute_name (str, optional): The name of the attribute
|
|
362
|
+
corresponding to the unit.
|
|
363
|
+
path (str, optional): The file path of the attribute for
|
|
364
|
+
logging purposes.
|
|
365
|
+
get_multiplier (bool, optional): Whether to return the multiplier
|
|
366
|
+
for the conversion. If False, returns the base unit combination.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
pint.Quantity or tuple:
|
|
370
|
+
If `get_multiplier` is False, returns the
|
|
371
|
+
`input_unit` converted to base units as a `pint.Quantity`.
|
|
372
|
+
If `get_multiplier` is True, returns the multiplier as a float
|
|
373
|
+
and the base units as a `pint.Quantity`.
|
|
264
374
|
"""
|
|
265
375
|
# convert attribute unit into unit combination of base units
|
|
266
376
|
combined_unit = None
|
|
@@ -277,9 +387,24 @@ class UnitHandling:
|
|
|
277
387
|
return attribute_unit_in_base_units
|
|
278
388
|
|
|
279
389
|
def consistency_checks_input_units(self, optimization_setup):
|
|
280
|
-
"""
|
|
281
|
-
|
|
282
|
-
|
|
390
|
+
"""
|
|
391
|
+
Performs unit consistency checks on the input data.
|
|
392
|
+
|
|
393
|
+
This method checks whether the units of the parameters defined in the
|
|
394
|
+
input CSV files are consistent with the system's dimensional framework.
|
|
395
|
+
It compares units across elements and technologies and ensures that the
|
|
396
|
+
units match the expected dimensional definitions. The check also
|
|
397
|
+
includes units for conversion factors, retrofit flow coupling factors,
|
|
398
|
+
and other related parameters.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
optimization_setup (OptimizationSetup): The setup object containing
|
|
402
|
+
information about the optimization problem, including elements,
|
|
403
|
+
technologies, and carriers.
|
|
404
|
+
|
|
405
|
+
Raises:
|
|
406
|
+
AssertionError: If unit inconsistencies are found in the input
|
|
407
|
+
files or optimization setup.
|
|
283
408
|
"""
|
|
284
409
|
if not optimization_setup.solver.check_unit_consistency:
|
|
285
410
|
return
|
|
@@ -344,11 +469,21 @@ class UnitHandling:
|
|
|
344
469
|
self.save_carrier_energy_quantities(optimization_setup)
|
|
345
470
|
|
|
346
471
|
def _check_for_power_power(self, energy_quantity_units, energy_quantity_units_check):
|
|
347
|
-
"""
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
472
|
+
"""
|
|
473
|
+
Adjusts conversion factors or retrofit flow coupling factor units from power/power to energy/energy if needed.
|
|
474
|
+
|
|
475
|
+
This helper method tries to resolve unit inconsistencies that might
|
|
476
|
+
arise due to the units of conversion factors or retrofit flow coupling
|
|
477
|
+
factors. If units are inconsistent and involve power terms, the method
|
|
478
|
+
attempts to change the units from "power/power" to "energy/energy" to
|
|
479
|
+
resolve the inconsistency. This is done since both types of units are
|
|
480
|
+
allowed as inputs.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
energy_quantity_units (dict): Dictionary containing the energy
|
|
484
|
+
quantity units for each attribute.
|
|
485
|
+
energy_quantity_units_check (dict): Dictionary of energy quantities
|
|
486
|
+
in base units to check for consistency.
|
|
352
487
|
"""
|
|
353
488
|
exclude_strings = ["conversion_factor", "retrofit_flow_coupling_factor"]
|
|
354
489
|
if self._is_inconsistent(energy_quantity_units_check) and not self._is_inconsistent(energy_quantity_units_check, exclude_strings=exclude_strings):
|
|
@@ -362,15 +497,31 @@ class UnitHandling:
|
|
|
362
497
|
energy_quantity_units_check[key] = energy_quantity_units_check[key] * self.ureg(time_base_unit).to_base_units().units
|
|
363
498
|
|
|
364
499
|
def assert_unit_consistency(self, elements, energy_quantity_units, energy_quantity_units_check, item, optimization_setup, reference_carrier_name, unit_dict):
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
500
|
+
"""
|
|
501
|
+
Asserts that the units of the attributes of an element are consistent
|
|
502
|
+
with the system's dimensional framework.
|
|
503
|
+
|
|
504
|
+
This method checks if the units of attributes defined in the input
|
|
505
|
+
files (or the optimization setup) are consistent with each other and
|
|
506
|
+
with the base units. It verifies that all the parameters' units
|
|
507
|
+
conform to dimensional analysis and resolves any inconsistencies.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
elements (list): List of all elements in the system.
|
|
511
|
+
energy_quantity_units (dict): Dictionary of attribute names and
|
|
512
|
+
their corresponding energy quantity units.
|
|
513
|
+
energy_quantity_units_check (dict): Dictionary of energy quantity
|
|
514
|
+
units in base units for consistency checking.
|
|
515
|
+
item: The specific element or energy system being checked.
|
|
516
|
+
optimization_setup (OptimizationSetup): The optimization setup
|
|
517
|
+
containing all system parameters.
|
|
518
|
+
reference_carrier_name (str): The name of the reference carrier
|
|
519
|
+
associated with the element (if applicable).
|
|
520
|
+
unit_dict (dict): Dictionary of unit specifications for attributes.
|
|
521
|
+
|
|
522
|
+
Raises:
|
|
523
|
+
AssertionError: If inconsistencies are found in the units of the
|
|
524
|
+
attributes.
|
|
374
525
|
"""
|
|
375
526
|
attributes_with_lowest_appearance = self._get_attributes_with_least_often_appearing_unit(energy_quantity_units)
|
|
376
527
|
# assert unit consistency
|
|
@@ -417,11 +568,26 @@ class UnitHandling:
|
|
|
417
568
|
f"The attribute units defined in the energy_system are not consistent! Most probably, the unit(s) of the attribute(s) {self._get_units_of_wrong_attributes(wrong_atts=energy_quantity_units, unit_dict=unit_dict)} are wrong.")
|
|
418
569
|
|
|
419
570
|
def _is_inconsistent(self, energy_quantity_units,exclude_strings=None):
|
|
420
|
-
"""
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
571
|
+
"""
|
|
572
|
+
Checks if the units of the attributes of an element are inconsistent.
|
|
573
|
+
|
|
574
|
+
This method identifies inconsistencies in the units of attributes
|
|
575
|
+
by comparing the energy quantity terms across all attributes. It allows
|
|
576
|
+
for the exclusion of certain attributes from the consistency check
|
|
577
|
+
based on the `exclude_strings` parameter.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
energy_quantity_units (dict): Dictionary containing attribute
|
|
581
|
+
names and their corresponding energy quantity terms (e.g.,
|
|
582
|
+
"kg/s", "m^2").
|
|
583
|
+
exclude_strings (list, optional): List of strings for which c
|
|
584
|
+
consistency is not checked (e.g., ["conversion_factor",
|
|
585
|
+
"retrofit_flow_coupling_factor"]).
|
|
586
|
+
|
|
587
|
+
Returns:
|
|
588
|
+
bool:
|
|
589
|
+
Returns `True` if there are inconsistencies (i.e., if the
|
|
590
|
+
energy quantity units differ), otherwise returns `False`.
|
|
425
591
|
"""
|
|
426
592
|
# exclude attributes which are not of interest for consistency
|
|
427
593
|
if exclude_strings:
|
|
@@ -433,11 +599,22 @@ class UnitHandling:
|
|
|
433
599
|
return False
|
|
434
600
|
|
|
435
601
|
def _get_units_of_wrong_attributes(self, wrong_atts, unit_dict):
|
|
436
|
-
"""
|
|
602
|
+
"""
|
|
603
|
+
Gets units of attributes showing wrong units.
|
|
604
|
+
|
|
605
|
+
This method retrieves the units in base units for attributes that have
|
|
606
|
+
inconsistent energy quantities based on the provided `wrong_atts`.
|
|
607
|
+
|
|
608
|
+
Args:
|
|
609
|
+
wrong_atts (dict): Dictionary containing attribute names with
|
|
610
|
+
inconsistent units.
|
|
611
|
+
unit_dict (dict): Dictionary of attribute names and their unit
|
|
612
|
+
specifications in base units.
|
|
437
613
|
|
|
438
|
-
:
|
|
439
|
-
|
|
440
|
-
|
|
614
|
+
Returns:
|
|
615
|
+
dict:
|
|
616
|
+
A dictionary where keys are attribute names, and values
|
|
617
|
+
are their corresponding units in base units.
|
|
441
618
|
"""
|
|
442
619
|
wrong_atts_with_units = {}
|
|
443
620
|
for att in wrong_atts:
|
|
@@ -445,12 +622,24 @@ class UnitHandling:
|
|
|
445
622
|
return wrong_atts_with_units
|
|
446
623
|
|
|
447
624
|
def _write_inconsistent_units_file(self, inconsistent_attributes, item_name, analysis, reference_carrier_name=None):
|
|
448
|
-
"""
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
625
|
+
"""
|
|
626
|
+
Writes a file documenting attributes and their units that cause unit
|
|
627
|
+
inconsistency.
|
|
628
|
+
|
|
629
|
+
This method writes a JSON file that contains a record of the
|
|
630
|
+
inconsistent attributes and their units for a given element or energy
|
|
631
|
+
system. This helps with identifying and resolving unit issues in the
|
|
632
|
+
system.
|
|
633
|
+
|
|
634
|
+
Args:
|
|
635
|
+
inconsistent_attributes (dict): Attributes that are inconsistent in
|
|
636
|
+
terms of their units.
|
|
637
|
+
item_name (str): The name of the element or energy system that has
|
|
638
|
+
inconsistent units.
|
|
639
|
+
analysis (dict): Dictionary containing analysis settings, including
|
|
640
|
+
output folder.
|
|
641
|
+
reference_carrier_name (str, optional): The name of the reference
|
|
642
|
+
carrier, if the item is a conversion technology.
|
|
454
643
|
"""
|
|
455
644
|
inconsistent_attributes_dict = {"element_name": item_name, "reference_carrier": reference_carrier_name, "attribute_names": str(inconsistent_attributes.keys())}
|
|
456
645
|
directory = os.path.join(analysis.folder_output, os.path.basename(analysis.dataset))
|
|
@@ -461,10 +650,20 @@ class UnitHandling:
|
|
|
461
650
|
json.dump(inconsistent_attributes_dict, json_file)
|
|
462
651
|
|
|
463
652
|
def _get_attributes_with_least_often_appearing_unit(self, energy_quantity_units):
|
|
464
|
-
"""
|
|
653
|
+
"""
|
|
654
|
+
Finds attributes that have the least commonly appearing unit.
|
|
465
655
|
|
|
466
|
-
|
|
467
|
-
|
|
656
|
+
This method identifies the attributes with the least frequent unit occurrence.
|
|
657
|
+
The assumption is that the least frequent unit is most likely the incorrect one.
|
|
658
|
+
|
|
659
|
+
Args:
|
|
660
|
+
energy_quantity_units (dict): Dictionary containing attribute names
|
|
661
|
+
and their corresponding energy quantity terms.
|
|
662
|
+
|
|
663
|
+
Returns:
|
|
664
|
+
dict:
|
|
665
|
+
A dictionary of attributes that have the least frequently
|
|
666
|
+
appearing units, along with their energy quantity terms.
|
|
468
667
|
"""
|
|
469
668
|
min_unit_count = np.inf
|
|
470
669
|
attributes_with_lowest_appearance = {}
|
|
@@ -478,10 +677,20 @@ class UnitHandling:
|
|
|
478
677
|
return attributes_with_lowest_appearance
|
|
479
678
|
|
|
480
679
|
def get_most_often_appearing_energy_unit(self, energy_units):
|
|
481
|
-
"""
|
|
680
|
+
"""
|
|
681
|
+
Finds the most commonly appearing energy unit for a carrier's attributes.
|
|
682
|
+
|
|
683
|
+
This method identifies the most frequently used energy unit across the
|
|
684
|
+
attributes of a given carrier, which is assumed to be the correct one.
|
|
482
685
|
|
|
483
|
-
:
|
|
484
|
-
|
|
686
|
+
Args:
|
|
687
|
+
energy_units (dict): Dictionary containing attribute names and their
|
|
688
|
+
energy quantity terms.
|
|
689
|
+
|
|
690
|
+
Returns:
|
|
691
|
+
str:
|
|
692
|
+
The energy unit that appears most frequently across the
|
|
693
|
+
attributes of the carrier.
|
|
485
694
|
"""
|
|
486
695
|
max_unit_count = 0
|
|
487
696
|
correct_value = None
|
|
@@ -494,14 +703,28 @@ class UnitHandling:
|
|
|
494
703
|
return correct_value
|
|
495
704
|
|
|
496
705
|
def _get_conversion_factor_units(self, conversion_element, unit_specs, reference_carrier, elements):
|
|
497
|
-
"""Splits conversion factor units into dependent carrier and reference carrier part
|
|
498
|
-
|
|
499
|
-
:param conversion_element: Conversion technology element the conversion factor belongs to
|
|
500
|
-
:param unit_specs: dict containing unit category and unit as pint Quantity in base units
|
|
501
|
-
:param reference_carrier: Carrier object of conversion_element's reference carrier
|
|
502
|
-
:param elements: list containing all existing elements
|
|
503
|
-
:return: dict of conversion_element's conversion factors' units separated by dependent carrier and reference carrier
|
|
504
706
|
"""
|
|
707
|
+
Splits conversion factor units into dependent and reference carrier units.
|
|
708
|
+
|
|
709
|
+
This method takes a conversion factor and splits its units into two parts:
|
|
710
|
+
one for the dependent carrier and one for the reference carrier. This is
|
|
711
|
+
necessary when dealing with complex unit formats like "MW/MW".
|
|
712
|
+
|
|
713
|
+
Args:
|
|
714
|
+
conversion_element (object): The conversion technology element the
|
|
715
|
+
conversion factor belongs to.
|
|
716
|
+
unit_specs (dict): Dictionary containing unit category and unit as
|
|
717
|
+
pint Quantity in base units.
|
|
718
|
+
reference_carrier (Carrier): The reference carrier object for the
|
|
719
|
+
conversion technology.
|
|
720
|
+
elements (list): List of all elements in the system, used to find
|
|
721
|
+
dependent carriers.
|
|
722
|
+
|
|
723
|
+
Returns:
|
|
724
|
+
dict:
|
|
725
|
+
A dictionary of conversion factor units separated by
|
|
726
|
+
dependent carrier and reference carrier.
|
|
727
|
+
"""
|
|
505
728
|
conversion_factor_units = {}
|
|
506
729
|
for dependent_carrier_name, cf_unit_specs in unit_specs.items():
|
|
507
730
|
assert cf_unit_specs["unit"] != "1", f"Since there doesn't exist a conversion_factor file for the technology {conversion_element.name}, the attribute conversion_factor_default must be defined with units to ensure unit consistency"
|
|
@@ -543,11 +766,23 @@ class UnitHandling:
|
|
|
543
766
|
return conversion_factor_units
|
|
544
767
|
|
|
545
768
|
def _get_number_of_division_signs_energy_quantity(self, carrier_units, power=False):
|
|
546
|
-
"""
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
769
|
+
"""
|
|
770
|
+
Counts the number of division signs in a carrier's energy or power unit.
|
|
771
|
+
|
|
772
|
+
This method counts the number of division signs ("/") in the most
|
|
773
|
+
common energy or power unit of a carrier's attributes. It helps
|
|
774
|
+
determine how energy or power is distributed across different units
|
|
775
|
+
in the system.
|
|
776
|
+
|
|
777
|
+
Args:
|
|
778
|
+
carrier_units (dict): The units of the carrier element.
|
|
779
|
+
power (bool, optional): If `True`, it counts the number of division
|
|
780
|
+
signs in the power unit (energy divided by time). Defaults to `False`.
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
int:
|
|
784
|
+
The number of division signs in the most common energy or
|
|
785
|
+
power unit.
|
|
551
786
|
"""
|
|
552
787
|
energy_units = {}
|
|
553
788
|
time_base_unit = [key for key, value in self.base_units.items() if value == "[time]"][0]
|
|
@@ -560,11 +795,21 @@ class UnitHandling:
|
|
|
560
795
|
return len(str(energy_unit_ref_carrier.units).split("/")) - 1
|
|
561
796
|
|
|
562
797
|
def _remove_non_energy_units(self, unit_specs, attribute_name):
|
|
563
|
-
"""
|
|
798
|
+
"""
|
|
799
|
+
Removes all non-energy dimensions from a unit by multiplication/division.
|
|
800
|
+
|
|
801
|
+
This method strips non-energy units (e.g., mass, time, etc.) from the
|
|
802
|
+
specified unit and leaves only the energy quantity part. This is used
|
|
803
|
+
for comparing energy quantities across different attributes.
|
|
804
|
+
|
|
805
|
+
Args:
|
|
806
|
+
unit_specs (dict): The unit specifications for the attribute.
|
|
807
|
+
attribute_name (str): The name of the attribute to be processed.
|
|
564
808
|
|
|
565
|
-
:
|
|
566
|
-
|
|
567
|
-
|
|
809
|
+
Returns:
|
|
810
|
+
dict:
|
|
811
|
+
A dictionary containing the attribute name and the
|
|
812
|
+
energy-only unit.
|
|
568
813
|
"""
|
|
569
814
|
# dictionary which assigns unit dimensions to corresponding base unit namings
|
|
570
815
|
distinct_dims = {"money": "[currency]", "distance": "[length]", "time": "[time]", "emissions": "[mass]"}
|
|
@@ -583,19 +828,35 @@ class UnitHandling:
|
|
|
583
828
|
|
|
584
829
|
def save_carrier_energy_quantities(self, optimization_setup):
|
|
585
830
|
"""
|
|
586
|
-
|
|
831
|
+
Saves energy quantity units of carriers after consistency checks.
|
|
587
832
|
|
|
588
|
-
|
|
589
|
-
|
|
833
|
+
This method stores the energy quantities of the carriers after they
|
|
834
|
+
have been verified for unit consistency. It ensures that the units of
|
|
835
|
+
the carrier's attributes are properly assigned to variables for later
|
|
836
|
+
use in calculations.
|
|
837
|
+
|
|
838
|
+
Args:
|
|
839
|
+
optimization_setup (OptimizationSetup): The optimization setup
|
|
840
|
+
containing system parameters.
|
|
841
|
+
|
|
842
|
+
Returns:
|
|
843
|
+
dict:
|
|
844
|
+
A dictionary containing the carrier units.
|
|
590
845
|
"""
|
|
591
846
|
for carrier in optimization_setup.dict_elements["Carrier"]:
|
|
592
847
|
self.carrier_energy_quantities[carrier.name] = self._remove_non_energy_units(carrier.units["demand"], attribute_name=None)[None]
|
|
593
848
|
|
|
594
849
|
def set_base_unit_combination(self, input_unit, attribute):
|
|
595
|
-
"""
|
|
850
|
+
"""
|
|
851
|
+
Converts the input unit to the corresponding base unit.
|
|
596
852
|
|
|
597
|
-
|
|
598
|
-
|
|
853
|
+
This method takes an input unit and converts it to its base unit
|
|
854
|
+
equivalent, which can be used for further unit analysis. It also handles
|
|
855
|
+
special cases where the input unit is `NaN` or dimensionless.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
input_unit (str or Quantity): The unit to be converted to base units.
|
|
859
|
+
attribute (str): The name of the attribute that uses the unit.
|
|
599
860
|
"""
|
|
600
861
|
# TODO combine overlap with get_unit_multiplier
|
|
601
862
|
# if input unit is already in base units --> the input unit is base unit
|
|
@@ -615,41 +876,73 @@ class UnitHandling:
|
|
|
615
876
|
self.dict_attribute_values[attribute] = {"base_combination": base_unit_combination, "values": None}
|
|
616
877
|
|
|
617
878
|
def set_attribute_values(self, df_output, attribute):
|
|
618
|
-
"""
|
|
879
|
+
"""
|
|
880
|
+
Saves the values of an attribute from a dataframe output.
|
|
619
881
|
|
|
620
|
-
|
|
621
|
-
|
|
882
|
+
This method stores the values of a given attribute from a dataframe into
|
|
883
|
+
the class' internal dictionary for future use.
|
|
884
|
+
|
|
885
|
+
Args:
|
|
886
|
+
df_output (DataFrame): The dataframe containing the output values for attributes.
|
|
887
|
+
attribute (str): The name of the attribute whose values are being saved.
|
|
622
888
|
"""
|
|
623
889
|
if attribute in self.dict_attribute_values.keys():
|
|
624
890
|
self.dict_attribute_values[attribute]["values"] = df_output
|
|
625
891
|
|
|
626
892
|
def check_if_invalid_hourstring(self, input_unit):
|
|
627
893
|
"""
|
|
628
|
-
|
|
894
|
+
Checks if "h" in the input unit refers to the Planck constant.
|
|
895
|
+
|
|
896
|
+
This method ensures that the string "h" is not mistaken for the
|
|
897
|
+
Planck constant when specifying time units in the system. It will raise
|
|
898
|
+
an error if "h" is used incorrectly.
|
|
629
899
|
|
|
630
|
-
:
|
|
900
|
+
Args:
|
|
901
|
+
input_unit (str): The unit string to be checked.
|
|
631
902
|
"""
|
|
632
903
|
_tuple_units = self.ureg(input_unit).to_tuple()[1]
|
|
633
904
|
_list_units = [_item[0] for _item in _tuple_units]
|
|
634
905
|
assert "planck_constant" not in _list_units, f"Error in input unit '{input_unit}'. Did you want to define hour? Use 'hour' instead of 'h' ('h' is interpreted as the planck constant)"
|
|
635
906
|
|
|
636
907
|
def define_ton_as_metric(self):
|
|
637
|
-
"""
|
|
908
|
+
"""
|
|
909
|
+
Redefines the "ton" as a metric ton.
|
|
910
|
+
|
|
911
|
+
This method redefines the unit "ton" to represent the metric ton, ensuring
|
|
912
|
+
consistency across the system when dealing with mass units.
|
|
913
|
+
"""
|
|
638
914
|
self.ureg.define("ton = metric_ton")
|
|
639
915
|
|
|
640
916
|
def redefine_standard_units(self):
|
|
641
|
-
"""
|
|
917
|
+
"""
|
|
918
|
+
Redefines standard units required in the system.
|
|
919
|
+
|
|
920
|
+
This method sets up standard units such as "Euro", "year", and "ton",
|
|
921
|
+
and ensures that the system handles leap years correctly.
|
|
922
|
+
"""
|
|
642
923
|
self.ureg.define("Euro = [currency] = EURO = Eur = €")
|
|
643
924
|
self.ureg.define("year = 365 * day = a = yr = julian_year")
|
|
644
925
|
self.ureg.define("ton = metric_ton")
|
|
645
926
|
|
|
646
927
|
@staticmethod
|
|
647
928
|
def check_pos_neg_boolean(array, axis=None):
|
|
648
|
-
"""
|
|
929
|
+
"""
|
|
930
|
+
Checks if the array contains only positive or negative booleans (-1, 0, 1).
|
|
649
931
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
932
|
+
This method verifies if the input array contains values that are either
|
|
933
|
+
positive or negative booleans, which is often used to check binary
|
|
934
|
+
states in the optimization.
|
|
935
|
+
|
|
936
|
+
Args:
|
|
937
|
+
array (numpy.ndarray): The numeric array to be checked.
|
|
938
|
+
axis (int, optional): The axis of the dataframe along which the
|
|
939
|
+
check is applied.
|
|
940
|
+
|
|
941
|
+
Returns:
|
|
942
|
+
bool:
|
|
943
|
+
Returns `True` if the array contains only positive or
|
|
944
|
+
negative booleans, otherwise returns `False`.
|
|
945
|
+
"""
|
|
653
946
|
if axis:
|
|
654
947
|
is_pos_neg_boolean = np.apply_along_axis(lambda row: np.array_equal(np.abs(row), np.abs(row).astype(bool)), 1, array).any()
|
|
655
948
|
else:
|