epyt-flow 0.1.1__py3-none-any.whl → 0.2.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.
@@ -2,6 +2,7 @@
2
2
  Module provides a class for implementing sensor configurations.
3
3
  """
4
4
  from copy import deepcopy
5
+ import warnings
5
6
  import numpy as np
6
7
  import epyt
7
8
 
@@ -20,6 +21,115 @@ SENSOR_TYPE_NODE_BULK_SPECIES = 9
20
21
  SENSOR_TYPE_LINK_BULK_SPECIES = 10
21
22
  SENSOR_TYPE_SURFACE_SPECIES = 11
22
23
 
24
+ AREA_UNIT_FT2 = 1
25
+ AREA_UNIT_M2 = 2
26
+ AREA_UNIT_CM2 = 3
27
+ MASS_UNIT_MG = 4
28
+ MASS_UNIT_UG = 5
29
+ MASS_UNIT_MOL = 6
30
+ MASS_UNIT_MMOL = 7
31
+ TIME_UNIT_HRS = 8
32
+
33
+
34
+ def areaunit_to_id(unit_desc: str) -> int:
35
+ """
36
+ Converts a given area units string to the corresponding ID.
37
+
38
+ Parameters
39
+ ----------
40
+ unit_desc : `str`
41
+ Area units string.
42
+
43
+ Returns
44
+ -------
45
+ `int`
46
+ Corresponding area unit ID.
47
+ """
48
+ return {"FT2": AREA_UNIT_FT2,
49
+ "M2": AREA_UNIT_M2,
50
+ "CM2": AREA_UNIT_CM2}[unit_desc]
51
+
52
+
53
+ def massunit_to_id(unit_desc: str) -> int:
54
+ """
55
+ Converts a given mass units string to the corresponding ID.
56
+
57
+ Parameters
58
+ ----------
59
+ unit_desc : `str`
60
+ Mass units string.
61
+
62
+ Returns
63
+ -------
64
+ `int`
65
+ Corresponding mass unit ID.
66
+ """
67
+ return {"MG": MASS_UNIT_MG,
68
+ "UG": MASS_UNIT_UG,
69
+ "MOL": MASS_UNIT_MOL,
70
+ "MMOL": MASS_UNIT_MMOL}[unit_desc]
71
+
72
+
73
+ def qualityunits_to_id(unit_desc: str) -> int:
74
+ """
75
+ Converts a given measurement unit description to the corresponding mass unit ID.
76
+
77
+ Parameters
78
+ ----------
79
+ unit_desc : `str`
80
+ Mass unit.
81
+
82
+ Returns
83
+ -------
84
+ `int`
85
+ Mass unit ID.
86
+
87
+ Will be either None (if no water quality analysis was set up) or
88
+ one of the following constants:
89
+
90
+ - MASS_UNIT_MG = 4 (mg/L)
91
+ - MASS_UNIT_UG = 5 (ug/L)
92
+ - TIME_UNIT_HRS = 8 (hrs)
93
+ """
94
+ if unit_desc == "mg/L":
95
+ return MASS_UNIT_MG
96
+ elif unit_desc == "ug/L":
97
+ return MASS_UNIT_UG
98
+ elif unit_desc == "hrs":
99
+ return TIME_UNIT_HRS
100
+ else:
101
+ return None
102
+
103
+
104
+ def qualityunits_to_str(unit_id: int) -> str:
105
+ """
106
+ Converts a given measurement unit ID to the corresponding description.
107
+
108
+ Parameters
109
+ ----------
110
+ unit_id : `int`
111
+ ID of the mass unit.
112
+
113
+ Must be one of the following constants:
114
+
115
+ - MASS_UNIT_MG = 4 (mg/L)
116
+ - MASS_UNIT_UG = 5 (ug/L)
117
+ - TIME_UNIT_HRS = 8 (hrs)
118
+
119
+ Returns
120
+ -------
121
+ `str`
122
+ Mass unit description.
123
+ """
124
+ if unit_id == MASS_UNIT_MG:
125
+ return "mg/L"
126
+ elif unit_id == MASS_UNIT_UG:
127
+ return "ug/L"
128
+ elif unit_id == TIME_UNIT_HRS:
129
+ return "hrs"
130
+ else:
131
+ raise ValueError(f"Unknown unit ID '{unit_id}'")
132
+
23
133
 
24
134
  @serializable(SENSOR_CONFIG_ID, ".epytflow_sensor_config")
25
135
  class SensorConfig(JsonSerializable):
@@ -39,7 +149,7 @@ class SensorConfig(JsonSerializable):
39
149
  tanks : `list[str]`
40
150
  List of all tanks (i.e. IDs) in the network.
41
151
  species : `list[str]`
42
- List of all (EPANET-MSX) species (i.e. IDs) in the network
152
+ List of all (EPANET-MSX) species (i.e. IDs) in the network
43
153
  pressure_sensors : `list[str]`, optional
44
154
  List of all nodes (i.e. IDs) at which a pressure sensor is placed.
45
155
 
@@ -131,9 +241,65 @@ class SensorConfig(JsonSerializable):
131
241
  sorted according to their EPANET index.
132
242
 
133
243
  The default is None.
244
+ flow_unit : `int`
245
+ Specifies the flow units and consequently all other hydraulic units
246
+ (US CUSTOMARY or SI METRIC) as well.
247
+
248
+ Must be one of the following EPANET toolkit constants:
249
+
250
+ - EN_CFS = 0 (cu foot/sec)
251
+ - EN_GPM = 1 (gal/min)
252
+ - EN_MGD = 2 (Million gal/day)
253
+ - EN_IMGD = 3 (Imperial MGD)
254
+ - EN_AFD = 4 (ac-foot/day)
255
+ - EN_LPS = 5 (liter/sec)
256
+ - EN_LPM = 6 (liter/min)
257
+ - EN_MLD = 7 (Megaliter/day)
258
+ - EN_CMH = 8 (cubic meter/hr)
259
+ - EN_CMD = 9 (cubic meter/day)
260
+ quality_unit : `str`, optional
261
+ Measurement unit (in a basic quality analysis) -- only relevant
262
+ if basic water quality is enabled.
263
+
264
+ Must be one of the following constants:
265
+
266
+ - MASS_UNIT_MG = 4 (mg/L)
267
+ - MASS_UNIT_UG = 5 (ug/L)
268
+ - TIME_UNIT_HRS = 8 (hrs)
269
+
270
+ bulk_species_mass_unit : `list[int]`, optional
271
+ Specifies the mass unit for each bulk species -- only relevant if EPANET-MSX is used.
272
+
273
+ Must be one of the following constants:
274
+
275
+ - MASS_UNIT_MG = 4 (milligram)
276
+ - MASS_UNIT_UG = 5 (microgram)
277
+ - MASS_UNIT_MOL = 6 (mole)
278
+ - MASS_UNIT_MMOL = 7 (millimole)
279
+
280
+ Note that the assumed ordering is the same as given in 'bulk_species'.
281
+ surface_species_mass_unit : `list[int]`, optional
282
+ Specifies the mass unit for each surface species -- only relevant if EPANET-MSX is used.
283
+
284
+ Must be one of the following constants:
285
+
286
+ - MASS_UNIT_MG = 4 (milligram)
287
+ - MASS_UNIT_UG = 5 (microgram)
288
+ - MASS_UNIT_MOL = 6 (mole)
289
+ - MASS_UNIT_MMOL = 7 (millimole)
290
+
291
+ Note that the assumed ordering is the same as given in 'surface_species'.
292
+ surface_species_area_unit : `int`, optional
293
+ Species the area unit of all surface species -- only relevant if EPANET-MSX is used.
294
+ Must be one of the following constants:
295
+
296
+ - AREA_UNIT_FT2 = 1 (square feet)
297
+ - AREA_UNIT_M2 = 2 (square meters)
298
+ - AREA_UNIT_CM2 = 3 (square centimeters)
134
299
  """
135
300
  def __init__(self, nodes: list[str], links: list[str], valves: list[str], pumps: list[str],
136
301
  tanks: list[str], bulk_species: list[str], surface_species: list[str],
302
+ flow_unit: int = None,
137
303
  pressure_sensors: list[str] = [],
138
304
  flow_sensors: list[str] = [],
139
305
  demand_sensors: list[str] = [],
@@ -148,7 +314,12 @@ class SensorConfig(JsonSerializable):
148
314
  node_id_to_idx: dict = None, link_id_to_idx: dict = None,
149
315
  valve_id_to_idx: dict = None, pump_id_to_idx: dict = None,
150
316
  tank_id_to_idx: dict = None, bulkspecies_id_to_idx: dict = None,
151
- surfacespecies_id_to_idx: dict = None, **kwds):
317
+ surfacespecies_id_to_idx: dict = None,
318
+ quality_unit: int = None,
319
+ bulk_species_mass_unit : list[int] = [],
320
+ surface_species_mass_unit : list[int] = [],
321
+ surface_species_area_unit : int = None,
322
+ **kwds):
152
323
  if not isinstance(nodes, list):
153
324
  raise TypeError("'nodes' must be an instance of 'list[str]' " +
154
325
  f"but not of '{type(nodes)}'")
@@ -329,6 +500,48 @@ class SensorConfig(JsonSerializable):
329
500
  if any(s not in surface_species for s in surfacespecies_id_to_idx.keys()):
330
501
  raise ValueError("Unknown surface species ID in 'surfacespecies_id_to_idx'")
331
502
 
503
+ if flow_unit is not None:
504
+ if not isinstance(flow_unit, int):
505
+ raise TypeError("'flow_unit' must be a an instance of 'int' " +
506
+ f"but not of '{type(flow_unit)}'")
507
+ if flow_unit not in range(10):
508
+ raise ValueError("Invalid value of 'flow_unit'")
509
+ else:
510
+ warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
511
+ " -- support of such old files will be removed in the next release!",
512
+ DeprecationWarning)
513
+
514
+ if quality_unit is not None:
515
+ if not isinstance(quality_unit, int):
516
+ raise TypeError("'quality_mass_unit' must be an instance of 'int' " +
517
+ f"but not of '{type(quality_unit)}'")
518
+ if quality_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, TIME_UNIT_HRS]:
519
+ raise ValueError("Invalid value of 'quality_unit'")
520
+
521
+ if len(bulk_species_mass_unit) != len(bulk_species):
522
+ raise ValueError("Inconsistency between 'bulk_species_mass_unit' and 'bulk_species'")
523
+ if any(not isinstance(mass_unit, int) for mass_unit in bulk_species_mass_unit):
524
+ raise TypeError("All items in 'bulk_species_mass_unit' must be an instance of 'int'")
525
+ if any(mass_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, MASS_UNIT_MOL, MASS_UNIT_MMOL]
526
+ for mass_unit in bulk_species_mass_unit):
527
+ raise ValueError("Invalid mass unit in 'bulk_species_mass_unit'")
528
+
529
+ if len(surface_species_mass_unit) != len(surface_species):
530
+ raise ValueError("Inconsistency between 'surface_species_mass_unit' " +
531
+ "and 'surface_species'")
532
+ if any(not isinstance(mass_unit, int) for mass_unit in surface_species_mass_unit):
533
+ raise TypeError("All items in 'surface_species_mass_unit' must be an instance of 'int'")
534
+ if any(mass_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, MASS_UNIT_MOL, MASS_UNIT_MMOL]
535
+ for mass_unit in surface_species_mass_unit):
536
+ raise ValueError("Invalid mass unit in 'surface_species_mass_unit'")
537
+
538
+ if surface_species_area_unit is not None:
539
+ if not isinstance(surface_species_area_unit, int):
540
+ raise TypeError("'surface_species_area_unit' must be a an instance of 'int' " +
541
+ f"but not of '{type(surface_species_area_unit)}'")
542
+ if surface_species_area_unit not in [AREA_UNIT_FT2, AREA_UNIT_M2, AREA_UNIT_CM2]:
543
+ raise ValueError("Invalid area unit 'surface_species_area_unit'")
544
+
332
545
  self.__nodes = nodes
333
546
  self.__links = links
334
547
  self.__valves = valves
@@ -354,11 +567,45 @@ class SensorConfig(JsonSerializable):
354
567
  self.__tank_id_to_idx = tank_id_to_idx
355
568
  self.__bulkspecies_id_to_idx = bulkspecies_id_to_idx
356
569
  self.__surfacespecies_id_to_idx = surfacespecies_id_to_idx
570
+ self.__flow_unit = flow_unit
571
+ self.__quality_unit = quality_unit
572
+ self.__bulk_species_mass_unit = bulk_species_mass_unit
573
+ self.__surface_species_mass_unit = surface_species_mass_unit
574
+ self.__surface_species_area_unit = surface_species_area_unit
357
575
 
358
576
  self.__compute_indices() # Compute indices
359
577
 
360
578
  super().__init__(**kwds)
361
579
 
580
+ @staticmethod
581
+ def create_empty_sensor_config(sensor_config):
582
+ """
583
+ Creates an empty sensor configuration from a given sensor configuration
584
+ -- i.e. a clone of the given sensor configuration except that no sensors are set.
585
+
586
+ Parameters
587
+ ----------
588
+ sensor_config : :class:`epyt_flow.simulation.sensor_config.SensorConfig`
589
+ Sensor configuration used as a basis.
590
+
591
+ Returns
592
+ -------
593
+ :class:`epyt_flow.simulation.sensor_config.SensorConfig`
594
+ Empty sensor configuration.
595
+ """
596
+ return SensorConfig(nodes=sensor_config.nodes,
597
+ links=sensor_config.links,
598
+ valves=sensor_config.valves,
599
+ pumps=sensor_config.pumps,
600
+ tanks=sensor_config.tanks,
601
+ flow_unit=sensor_config.flow_unit,
602
+ quality_unit=sensor_config.quality_unit,
603
+ bulk_species=sensor_config.bulk_species,
604
+ surface_species=sensor_config.surface_species,
605
+ bulk_species_mass_unit=sensor_config.bulk_species_mass_unit,
606
+ surface_species_mass_unit=sensor_config.surface_species_mass_unit,
607
+ surface_species_area_unit=sensor_config.surface_species_area_unit)
608
+
362
609
  def node_id_to_idx(self, node_id: str) -> int:
363
610
  """
364
611
  Gets the index of a given node ID.
@@ -701,6 +948,50 @@ class SensorConfig(JsonSerializable):
701
948
  """
702
949
  return self.__tanks.copy()
703
950
 
951
+ @property
952
+ def flow_unit(self) -> int:
953
+ """
954
+ Gets the flow units.
955
+ Note that this specifies all other hydraulic units as well.
956
+
957
+ Will be one of the following EPANET toolkit constants:
958
+
959
+ - EN_CFS = 0 (cu foot/sec)
960
+ - EN_GPM = 1 (gal/min)
961
+ - EN_MGD = 2 (Million gal/day)
962
+ - EN_IMGD = 3 (Imperial MGD)
963
+ - EN_AFD = 4 (ac-foot/day)
964
+ - EN_LPS = 5 (liter/sec)
965
+ - EN_LPM = 6 (liter/min)
966
+ - EN_MLD = 7 (Megaliter/day)
967
+ - EN_CMH = 8 (cubic meter/hr)
968
+ - EN_CMD = 9 (cubic meter/day)
969
+
970
+ Returns
971
+ -------
972
+ `int`
973
+ Flow unit ID.
974
+ """
975
+ return self.__flow_unit
976
+
977
+ @property
978
+ def quality_unit(self) -> int:
979
+ """
980
+ Gets the measurement unit ID used in the basic quality analysis.
981
+
982
+ Will be one of the following constants:
983
+
984
+ - MASS_UNIT_MG = 4 (milligram)
985
+ - MASS_UNIT_UG = 5 (microgram)
986
+ - TIME_UNIT_HRS = 6 (hours)
987
+
988
+ Returns
989
+ -------
990
+ `int`
991
+ Mass unit ID.
992
+ """
993
+ return self.__quality_unit
994
+
704
995
  @property
705
996
  def bulk_species(self) -> list[str]:
706
997
  """
@@ -725,6 +1016,62 @@ class SensorConfig(JsonSerializable):
725
1016
  """
726
1017
  return self.__surface_species.copy()
727
1018
 
1019
+ @property
1020
+ def bulk_species_mass_unit(self) -> list[int]:
1021
+ """
1022
+ Gets the mass unit of each bulk species.
1023
+
1024
+ Will be one of the following constants:
1025
+
1026
+ - MASS_UNIT_MG = 4 (milligram)
1027
+ - MASS_UNIT_UG = 5 (microgram)
1028
+ - MASS_UNIT_MOL = 6 (mole)
1029
+ - MASS_UNIT_MMOL = 7 (millimole)
1030
+
1031
+ Returns
1032
+ -------
1033
+ `int`
1034
+ Mass unit ID.
1035
+ """
1036
+ return self.__bulk_species_mass_unit
1037
+
1038
+ @property
1039
+ def surface_species_mass_unit(self) -> list[int]:
1040
+ """
1041
+ Gets the mass unit of each surface species.
1042
+
1043
+ Will be one of the following constants:
1044
+
1045
+ - MASS_UNIT_MG = 4 (milligram)
1046
+ - MASS_UNIT_UG = 5 (microgram)
1047
+ - MASS_UNIT_MOL = 6 (mole)
1048
+ - MASS_UNIT_MMOL = 7 (millimole)
1049
+
1050
+ Returns
1051
+ -------
1052
+ `int`
1053
+ Mass unit ID.
1054
+ """
1055
+ return self.__surface_species_mass_unit
1056
+
1057
+ @property
1058
+ def surface_species_area_unit(self) -> int:
1059
+ """
1060
+ Gets the surface species area unit.
1061
+
1062
+ Will be one of the following constants:
1063
+
1064
+ - AREA_UNIT_FT2 = 1 (square feet)
1065
+ - AREA_UNIT_M2 = 2 (square meters)
1066
+ - AREA_UNIT_CM2 = 3 (square centimeters)
1067
+
1068
+ Returns
1069
+ -------
1070
+ `int`
1071
+ Area unit ID.
1072
+ """
1073
+ return self.__surface_species_area_unit
1074
+
728
1075
  @property
729
1076
  def pressure_sensors(self) -> list[str]:
730
1077
  """
@@ -1042,7 +1389,12 @@ class SensorConfig(JsonSerializable):
1042
1389
  "pump_id_to_idx": self.__pump_id_to_idx,
1043
1390
  "tank_id_to_idx": self.__tank_id_to_idx,
1044
1391
  "bulkspecies_id_to_idx": self.__bulkspecies_id_to_idx,
1045
- "surfacespecies_id_to_idx": self.__surfacespecies_id_to_idx}
1392
+ "surfacespecies_id_to_idx": self.__surfacespecies_id_to_idx,
1393
+ "flow_unit": self.__flow_unit,
1394
+ "quality_unit": self.__quality_unit,
1395
+ "bulk_species_mass_unit": self.__bulk_species_mass_unit,
1396
+ "surface_species_mass_unit": self.__surface_species_mass_unit,
1397
+ "surface_species_area_unit": self.__surface_species_area_unit}
1046
1398
 
1047
1399
  return super().get_attributes() | attr
1048
1400
 
@@ -1065,7 +1417,12 @@ class SensorConfig(JsonSerializable):
1065
1417
  and self.__tank_volume_sensors == other.tank_volume_sensors \
1066
1418
  and self.__bulk_species_node_sensors == other.bulk_species_node_sensors \
1067
1419
  and self.__bulk_species_link_sensors == other.bulk_species_link_sensors \
1068
- and self.__surface_species_sensors == other.surface_species_sensors
1420
+ and self.__surface_species_sensors == other.surface_species_sensors \
1421
+ and self.__flow_unit == other.flow_unit \
1422
+ and self.__quality_unit == other.quality_unit \
1423
+ and self.__bulk_species_mass_unit == other.bulk_species_mass_unit \
1424
+ and self.__surface_species_mass_unit == other.surface_species_mass_unit \
1425
+ and self.__surface_species_area_unit == other.surface_species_area_unit
1069
1426
 
1070
1427
  def __str__(self) -> str:
1071
1428
  return f"nodes: {self.__nodes} links: {self.__links} valves: {self.__valves} " +\
@@ -1077,10 +1434,14 @@ class SensorConfig(JsonSerializable):
1077
1434
  f"quality_link_sensors: {self.__quality_link_sensors} " +\
1078
1435
  f"valve_state_sensors: {self.__valve_state_sensors} " +\
1079
1436
  f"pump_state_sensors: {self.__pump_state_sensors} " +\
1080
- f"tank_volume_sensors: {self.__tank_volume_sensors}" +\
1081
- f"bulk_species_node_sensors: {self.__bulk_species_node_sensors}" +\
1082
- f"bulk_species_link_sensors: {self.__bulk_species_link_sensors}" +\
1083
- f"surface_species_sensors: {self.__surface_species_sensors}"
1437
+ f"tank_volume_sensors: {self.__tank_volume_sensors} " +\
1438
+ f"bulk_species_node_sensors: {self.__bulk_species_node_sensors} " +\
1439
+ f"bulk_species_link_sensors: {self.__bulk_species_link_sensors} " +\
1440
+ f"surface_species_sensors: {self.__surface_species_sensors} " +\
1441
+ f"flow_unit: {self.__flow_unit} quality_unit: {self.__quality_unit}" +\
1442
+ f"bulk_species_mass_unit: {self.__bulk_species_mass_unit} " +\
1443
+ f"surface_species_mass_unit: {self.__surface_species_mass_unit} " +\
1444
+ f"surface_species_area_unit: {self.__surface_species_area_unit}"
1084
1445
 
1085
1446
  def compute_readings(self, pressures: np.ndarray, flows: np.ndarray, demands: np.ndarray,
1086
1447
  nodes_quality: np.ndarray, links_quality: np.ndarray,
epyt_flow/topology.py CHANGED
@@ -1,6 +1,8 @@
1
1
  """
2
2
  Module provides a class for representing the topology of WDN.
3
3
  """
4
+ from copy import deepcopy
5
+ import warnings
4
6
  import numpy as np
5
7
  import networkx as nx
6
8
  from scipy.sparse import bsr_array
@@ -8,6 +10,10 @@ from scipy.sparse import bsr_array
8
10
  from .serialization import serializable, JsonSerializable, NETWORK_TOPOLOGY_ID
9
11
 
10
12
 
13
+ UNITS_USCUSTOM = 0
14
+ UNITS_SIMETRIC = 1
15
+
16
+
11
17
  @serializable(NETWORK_TOPOLOGY_ID, ".epytflow_topology")
12
18
  class NetworkTopology(nx.Graph, JsonSerializable):
13
19
  """
@@ -22,13 +28,28 @@ class NetworkTopology(nx.Graph, JsonSerializable):
22
28
  links : `list[tuple[tuple[str, str], dict]]`
23
29
  List of all links/pipes -- i.e. link ID, ID of connecting nodes, and link information
24
30
  such as pipe diameter, length, etc.
31
+ units : `int`
32
+ Measurement units category.
33
+
34
+ Must be one of the following constants:
35
+
36
+ - UNITS_USCUSTOM = 0 (US Customary)
37
+ - UNITS_SIMETRIC = 1 (SI Metric)
25
38
  """
26
39
  def __init__(self, f_inp: str, nodes: list[tuple[str, dict]],
27
- links: list[tuple[str, tuple[str, str], dict]], **kwds):
40
+ links: list[tuple[str, tuple[str, str], dict]],
41
+ units: int = None,
42
+ **kwds):
28
43
  super().__init__(name=f_inp, **kwds)
29
44
 
30
45
  self.__nodes = nodes
31
46
  self.__links = links
47
+ self.__units = units
48
+
49
+ if units is None:
50
+ warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
51
+ " -- support of such old files will be removed in the next release!",
52
+ DeprecationWarning)
32
53
 
33
54
  for node_id, node_info in nodes:
34
55
  node_elevation = node_info["elevation"]
@@ -104,23 +125,43 @@ class NetworkTopology(nx.Graph, JsonSerializable):
104
125
 
105
126
  raise ValueError(f"Unknown link '{link_id}'")
106
127
 
128
+ @property
129
+ def units(self) -> int:
130
+ """
131
+ Gets the used measurement units category.
132
+
133
+ Will be one of the following constants:
134
+
135
+ - UNITS_USCUSTOM = 0 (US Customary)
136
+ - UNITS_SIMETRIC = 1 (SI Metric)
137
+
138
+ Returns
139
+ -------
140
+ `int`
141
+ Measurement units category.
142
+ """
143
+ return self.__units
144
+
107
145
  def __eq__(self, other) -> bool:
108
146
  if not isinstance(other, NetworkTopology):
109
147
  raise TypeError("Can not compare 'NetworkTopology' instance to " +
110
148
  f"'{type(other)}' instance")
111
149
 
112
150
  return super().__eq__(other) and \
113
- self.get_all_nodes() == other.get_all_nodes() and \
114
- all(link_a[0] == link_b[0] and all(link_a[1] == link_b[1])
115
- for link_a, link_b in zip(self.get_all_links(), other.get_all_links()))
151
+ self.get_all_nodes() == other.get_all_nodes() \
152
+ and all(link_a[0] == link_b[0] and all(link_a[1] == link_b[1])
153
+ for link_a, link_b in zip(self.get_all_links(), other.get_all_links())) \
154
+ and self.__units == other.units
116
155
 
117
156
  def __str__(self) -> str:
118
- return f"f_inp: {self.name} nodes: {self.__nodes} links: {self.__links}"
157
+ return f"f_inp: {self.name} nodes: {self.__nodes} links: {self.__links} " +\
158
+ f"units: {self.__units}"
119
159
 
120
160
  def get_attributes(self) -> dict:
121
161
  return super().get_attributes() | {"f_inp": self.name,
122
162
  "nodes": self.__nodes,
123
- "links": self.__links}
163
+ "links": self.__links,
164
+ "units": self.__units}
124
165
 
125
166
  def get_adj_matrix(self) -> bsr_array:
126
167
  """