zen-garden 2.8.13__py3-none-any.whl → 2.9.1__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.
@@ -20,14 +20,38 @@ from zen_garden.utils import get_label_position
20
20
 
21
21
  class UnitHandling:
22
22
  """
23
- Class containing the unit handling procedure
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
- """ initialization of the unit_handling instance
28
-
29
- :param folder_path: The path to the folder containing the system specifications
30
- :param round_decimal_points: rounding tolerance
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
- """ gets base units of energy system """
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 ~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"
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
- """ extracts base units of energy system
105
-
106
- :return list_base_units: list of base units """
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
- """ calculates the combined unit for converting an input_unit to the base units
121
-
122
- :param input_unit: string of input unit
123
- :param return_combination: If True, return the combination of units
124
- :return combined_unit: multiplication factor """
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
- """ calculates the multiplier for converting an input_unit to the base units
227
-
228
- :param input_unit: string of input unit
229
- :param attribute_name: name of attribute
230
- :param path: path of element
231
- :param combined_unit: input unit expressed in base units
232
- :return multiplier: multiplication factor """
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
- """Converts the input_unit into base units and returns the multiplier such that the combined unit mustn't be computed twice
258
-
259
- :param input_unit: unit read from input csv files
260
- :param attribute_name: name of the attribute the input_unit corresponds to
261
- :param path: path of the attribute's csv file
262
- :param get_multiplier: bool whether multiplier should be returned or not
263
- :return: multiplier to convert input_unit to base units, pint Quantity of input_unit converted to base units
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
- """Checks if the units of the parameters specified in the input csv files are consistent
281
-
282
- :param optimization_setup: OptimizationSetup object
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
- """if unit consistency is not fulfilled because of conversion factor or retrofit_flow_coupling_factor,
348
- try to change "wrong" conversion factor or retrofit_flow_coupling_factor units from power/power to energy/energy (since both is allowed)
349
-
350
- :param energy_quantity_units: dict containing attribute names and their energy quantity units
351
- :param energy_quantity_units_check: dict containing the energy quantity terms in base units for checking consistency
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
- """Asserts that the units of the attributes of an element are consistent
366
-
367
- :param elements: list of all elements
368
- :param energy_quantity_units: dict containing attribute names and their energy quantity terms
369
- :param energy_quantity_units_check: dict containing the energy quantity terms in base units for checking consistency
370
- :param item: element or energy system
371
- :param optimization_setup: OptimizationSetup object
372
- :param reference_carrier_name: name of reference carrier if item is a conversion technology
373
- :param unit_dict: dict containing attribute names along with their units in base units
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
- """Checks if the units of the attributes of an element are inconsistent
421
-
422
- :param energy_quantity_units: dict containing attribute names and their energy quantity terms
423
- :param exclude_strings: string for which consistency is not checked
424
- :return: bool whether the units are consistent or not
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
- """Gets units of attributes showing wrong units
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
- :param wrong_atts: dict containing attribute names along with their energy_quantity part of attributes which have inconsistent units
439
- :param unit_dict: dict containing attribute names along with their units in base units
440
- :return: dict containing attribute names along with their unit in base unit of attributes which have inconsistent units
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
- """Writes file of attributes and their units which cause unit inconsistency
449
-
450
- :param inconsistent_attributes: attributes which are not consistent
451
- :param item_name: element name or energy system name which shows inconsistent units
452
- :param analysis: dictionary defining the analysis settings
453
- :param reference_carrier_name: name of reference carrier if item is a conversion technology
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
- """Finds all attributes which have the least often appearing unit
653
+ """
654
+ Finds attributes that have the least commonly appearing unit.
465
655
 
466
- :param energy_quantity_units: dict containing attribute names and their energy quantity terms
467
- :return: attribute names and energy quantity terms which appear the least often in energy_quantity_units
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
- """finds a carriers most likely correct energy unit
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
- :param energy_units: all the energy_quantity terms of a carriers attributes
484
- :return: most frequently appearing energy quantity
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
- """ Finds the most common energy quantity of a carrier and counts its number of division signs (or the number of division signs of the resulting power unit)
547
-
548
- :param carrier_units: unit attribute of the underlying carrier element
549
- :param power: bool to get the number of division signs of the most common power quantity (energy quantity divided by time)
550
- :return: number of division signs of the carriers most common energy/power unit
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
- """Removes all non-energy dimensions from unit by multiplication/division
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
- :param unit_specs: dict containing unit category and unit as pint Quantity in base units
566
- :param attribute_name: name of attribute whose unit is reduced to energy unit
567
- :return: dict with attribute name and reduced unit
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
- saves energy_quantity units of carriers after consistency checks in order to assign units to the variables later on
831
+ Saves energy quantity units of carriers after consistency checks.
587
832
 
588
- :param optimization_setup: optimization setup object
589
- :return: dict of carrier units
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
- """ converts the input unit to the corresponding base unit
850
+ """
851
+ Converts the input unit to the corresponding base unit.
596
852
 
597
- :param input_unit: unit of input
598
- :param attribute: name of attribute
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
- """ saves the attributes values of an attribute
879
+ """
880
+ Saves the values of an attribute from a dataframe output.
619
881
 
620
- :param df_output: output dataframe
621
- :param attribute: attribute name
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
- checks if "h" and thus "planck_constant" in input_unit
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
- :param input_unit: string of input_unit
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
- """ redefines the "ton" as a metric ton """
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
- """ defines the standard units always required in ZEN and removes the rounding error for leap years."""
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
- """ checks if the array has only positive or negative booleans (-1,0,1).
929
+ """
930
+ Checks if the array contains only positive or negative booleans (-1, 0, 1).
649
931
 
650
- :param array: numeric numpy array
651
- :param axis: axis of dataframe
652
- :return is_pos_neg_boolean: """
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: