epyt-flow 0.2.0__py3-none-any.whl → 0.4.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.
@@ -3,8 +3,10 @@ Module provides a class for implementing sensor configurations.
3
3
  """
4
4
  from copy import deepcopy
5
5
  import warnings
6
+ import itertools
6
7
  import numpy as np
7
8
  import epyt
9
+ from epyt.epanet import ToolkitConstants
8
10
 
9
11
  from ..serialization import SENSOR_CONFIG_ID, JsonSerializable, serializable
10
12
 
@@ -70,7 +72,7 @@ def massunit_to_id(unit_desc: str) -> int:
70
72
  "MMOL": MASS_UNIT_MMOL}[unit_desc]
71
73
 
72
74
 
73
- def qualityunits_to_id(unit_desc: str) -> int:
75
+ def qualityunit_to_id(unit_desc: str) -> int:
74
76
  """
75
77
  Converts a given measurement unit description to the corresponding mass unit ID.
76
78
 
@@ -87,8 +89,8 @@ def qualityunits_to_id(unit_desc: str) -> int:
87
89
  Will be either None (if no water quality analysis was set up) or
88
90
  one of the following constants:
89
91
 
90
- - MASS_UNIT_MG = 4 (mg/L)
91
- - MASS_UNIT_UG = 5 (ug/L)
92
+ - MASS_UNIT_MG = 4 (mg/L)
93
+ - MASS_UNIT_UG = 5 (ug/L)
92
94
  - TIME_UNIT_HRS = 8 (hrs)
93
95
  """
94
96
  if unit_desc == "mg/L":
@@ -101,19 +103,107 @@ def qualityunits_to_id(unit_desc: str) -> int:
101
103
  return None
102
104
 
103
105
 
104
- def qualityunits_to_str(unit_id: int) -> str:
106
+ def massunit_to_str(unit_id: int) -> str:
105
107
  """
106
- Converts a given measurement unit ID to the corresponding description.
108
+ Converts a given mass unit ID to the corresponding description.
107
109
 
108
110
  Parameters
109
111
  ----------
110
112
  unit_id : `int`
111
- ID of the mass unit.
113
+ ID of the flow unit.
114
+
115
+ Must be one of the following constant:
116
+
117
+ - MASS_UNIT_MG = 4
118
+ - MASS_UNIT_UG = 5
119
+ - MASS_UNIT_MOL = 6
120
+ - MASS_UNIT_MMOL = 7
121
+
122
+ Returns
123
+ -------
124
+ `str`
125
+ Mass unit description.
126
+ """
127
+ if unit_id is None:
128
+ return ""
129
+ elif unit_id == MASS_UNIT_MG:
130
+ return "MG"
131
+ elif unit_id == MASS_UNIT_UG:
132
+ return "UG"
133
+ elif unit_id == MASS_UNIT_MOL:
134
+ return "MOL"
135
+ elif unit_id == MASS_UNIT_MMOL:
136
+ return "MMOL"
137
+ else:
138
+ raise ValueError(f"Unknown mass unit ID '{unit_id}'")
139
+
140
+
141
+ def flowunit_to_str(unit_id: int) -> str:
142
+ """
143
+ Converts a given flow unit ID to the corresponding description.
144
+
145
+ Parameters
146
+ ----------
147
+ unit_id : `int`
148
+ ID of the flow unit.
149
+
150
+ Must be one of the following EPANET toolkit constants:
151
+
152
+ - EN_CFS = 0 (cubic foot/sec)
153
+ - EN_GPM = 1 (gal/min)
154
+ - EN_MGD = 2 (Million gal/day)
155
+ - EN_IMGD = 3 (Imperial MGD)
156
+ - EN_AFD = 4 (ac-foot/day)
157
+ - EN_LPS = 5 (liter/sec)
158
+ - EN_LPM = 6 (liter/min)
159
+ - EN_MLD = 7 (Megaliter/day)
160
+ - EN_CMH = 8 (cubic meter/hr)
161
+ - EN_CMD = 9 (cubic meter/day)
162
+
163
+ Returns
164
+ -------
165
+ `str`
166
+ Flow unit description.
167
+ """
168
+ if unit_id is None:
169
+ return ""
170
+ elif unit_id == ToolkitConstants.EN_CFS:
171
+ return "cubic foot/sec"
172
+ elif unit_id == ToolkitConstants.EN_GPM:
173
+ return "gal/min"
174
+ elif unit_id == ToolkitConstants.EN_MGD:
175
+ return "Million gal/day"
176
+ elif unit_id == ToolkitConstants.EN_IMGD:
177
+ return "Imperial MGD"
178
+ elif unit_id == ToolkitConstants.EN_AFD:
179
+ return "ac-foot/day"
180
+ elif unit_id == ToolkitConstants.EN_LPS:
181
+ return "liter/sec"
182
+ elif unit_id == ToolkitConstants.EN_LPM:
183
+ return "liter/min"
184
+ elif unit_id == ToolkitConstants.EN_MLD:
185
+ return "Megaliter/day"
186
+ elif unit_id == ToolkitConstants.EN_CMH:
187
+ return "cubic meter/hr"
188
+ elif unit_id == ToolkitConstants.EN_CMD:
189
+ return "cubic meter/day"
190
+ else:
191
+ raise ValueError(f"Unknown unit ID '{unit_id}'")
192
+
193
+
194
+ def qualityunit_to_str(unit_id: int) -> str:
195
+ """
196
+ Converts a given quality measurement unit ID to the corresponding description.
197
+
198
+ Parameters
199
+ ----------
200
+ unit_id : `int`
201
+ ID of the quality unit.
112
202
 
113
203
  Must be one of the following constants:
114
204
 
115
- - MASS_UNIT_MG = 4 (mg/L)
116
- - MASS_UNIT_UG = 5 (ug/L)
205
+ - MASS_UNIT_MG = 4 (mg/L)
206
+ - MASS_UNIT_UG = 5 (ug/L)
117
207
  - TIME_UNIT_HRS = 8 (hrs)
118
208
 
119
209
  Returns
@@ -121,7 +211,9 @@ def qualityunits_to_str(unit_id: int) -> str:
121
211
  `str`
122
212
  Mass unit description.
123
213
  """
124
- if unit_id == MASS_UNIT_MG:
214
+ if unit_id is None:
215
+ return ""
216
+ elif unit_id == MASS_UNIT_MG:
125
217
  return "mg/L"
126
218
  elif unit_id == MASS_UNIT_UG:
127
219
  return "ug/L"
@@ -131,6 +223,69 @@ def qualityunits_to_str(unit_id: int) -> str:
131
223
  raise ValueError(f"Unknown unit ID '{unit_id}'")
132
224
 
133
225
 
226
+ def areaunit_to_str(unit_id: int) -> str:
227
+ """
228
+ Converts a given area measurement unit ID to the corresponding description.
229
+
230
+ Parameters
231
+ ----------
232
+ unit_id : `int`
233
+ ID of the area unit.
234
+
235
+ Must be one of the following constants:
236
+
237
+ - AREA_UNIT_FT2 = 1
238
+ - AREA_UNIT_M2 = 2
239
+ - AREA_UNIT_CM2 = 3
240
+
241
+ Returns
242
+ -------
243
+ `str`
244
+ Area unit description.
245
+ """
246
+ if unit_id is None:
247
+ return None
248
+ elif unit_id == AREA_UNIT_FT2:
249
+ return "FT2"
250
+ elif unit_id == AREA_UNIT_M2:
251
+ return "M2"
252
+ elif unit_id == AREA_UNIT_CM2:
253
+ return "CM2"
254
+ else:
255
+ raise ValueError(f"Unknown unit ID '{unit_id}'")
256
+
257
+
258
+ def is_flowunit_simetric(unit_id: int) -> bool:
259
+ """
260
+ Checks if a given flow unit belongs to SI metric units.
261
+
262
+ Parameters
263
+ ----------
264
+ unit_id : `int`
265
+ ID of the flow unit.
266
+
267
+ Must be one of the following EPANET toolkit constants:
268
+
269
+ - EN_CFS = 0 (cubic foot/sec)
270
+ - EN_GPM = 1 (gal/min)
271
+ - EN_MGD = 2 (Million gal/day)
272
+ - EN_IMGD = 3 (Imperial MGD)
273
+ - EN_AFD = 4 (ac-foot/day)
274
+ - EN_LPS = 5 (liter/sec)
275
+ - EN_LPM = 6 (liter/min)
276
+ - EN_MLD = 7 (Megaliter/day)
277
+ - EN_CMH = 8 (cubic meter/hr)
278
+ - EN_CMD = 9 (cubic meter/day)
279
+
280
+ Returns
281
+ -------
282
+ `bool`
283
+ True if the fiven unit is a SI metric unit, False otherwise.
284
+ """
285
+ return unit_id in [ToolkitConstants.EN_LPM, ToolkitConstants.EN_MLD,
286
+ ToolkitConstants.EN_CMH, ToolkitConstants.EN_CMD]
287
+
288
+
134
289
  @serializable(SENSOR_CONFIG_ID, ".epytflow_sensor_config")
135
290
  class SensorConfig(JsonSerializable):
136
291
  """
@@ -247,7 +402,7 @@ class SensorConfig(JsonSerializable):
247
402
 
248
403
  Must be one of the following EPANET toolkit constants:
249
404
 
250
- - EN_CFS = 0 (cu foot/sec)
405
+ - EN_CFS = 0 (cubic foot/sec)
251
406
  - EN_GPM = 1 (gal/min)
252
407
  - EN_MGD = 2 (Million gal/day)
253
408
  - EN_IMGD = 3 (Imperial MGD)
@@ -430,7 +585,8 @@ class SensorConfig(JsonSerializable):
430
585
  if any(bulk_species_id not in bulk_species
431
586
  for bulk_species_id in bulk_species_node_sensors.keys()):
432
587
  raise ValueError("Unknown bulk species ID in 'bulk_species_node_sensors'")
433
- if any(node_id not in nodes for node_id in bulk_species_node_sensors.values()):
588
+ if any(node_id not in nodes for node_id in list(itertools.chain(
589
+ *bulk_species_node_sensors.values()))):
434
590
  raise ValueError("Unknown node ID in 'bulk_species_node_sensors'")
435
591
 
436
592
  if not isinstance(bulk_species_link_sensors, dict):
@@ -439,7 +595,8 @@ class SensorConfig(JsonSerializable):
439
595
  if any(bulk_species_id not in bulk_species
440
596
  for bulk_species_id in bulk_species_link_sensors.keys()):
441
597
  raise ValueError("Unknown bulk species ID in 'bulk_species_link_sensors'")
442
- if any(link_id not in links for link_id in bulk_species_link_sensors.values()):
598
+ if any(link_id not in links for link_id in list(itertools.chain(
599
+ *bulk_species_link_sensors.values()))):
443
600
  raise ValueError("Unknown link/pipe ID in 'bulk_species_link_sensors'")
444
601
 
445
602
  if not isinstance(surface_species_sensors, dict):
@@ -448,7 +605,8 @@ class SensorConfig(JsonSerializable):
448
605
  if any(surface_species_id not in surface_species_sensors
449
606
  for surface_species_id in surface_species_sensors.keys()):
450
607
  raise ValueError("Unknown surface species ID in 'surface_species_sensors'")
451
- if any(link_id not in links for link_id in surface_species_sensors.values()):
608
+ if any(link_id not in links for link_id in list(itertools.chain(
609
+ *surface_species_sensors.values()))):
452
610
  raise ValueError("Unknown link ID in 'surface_species_sensors'")
453
611
 
454
612
  if node_id_to_idx is not None:
@@ -585,7 +743,7 @@ class SensorConfig(JsonSerializable):
585
743
 
586
744
  Parameters
587
745
  ----------
588
- sensor_config : :class:`epyt_flow.simulation.sensor_config.SensorConfig`
746
+ sensor_config : :class:`~epyt_flow.simulation.sensor_config.SensorConfig`
589
747
  Sensor configuration used as a basis.
590
748
 
591
749
  Returns
@@ -604,9 +762,128 @@ class SensorConfig(JsonSerializable):
604
762
  surface_species=sensor_config.surface_species,
605
763
  bulk_species_mass_unit=sensor_config.bulk_species_mass_unit,
606
764
  surface_species_mass_unit=sensor_config.surface_species_mass_unit,
607
- surface_species_area_unit=sensor_config.surface_species_area_unit)
765
+ surface_species_area_unit=sensor_config.surface_species_area_unit,
766
+ node_id_to_idx=sensor_config.node_id_to_idx,
767
+ link_id_to_idx=sensor_config.link_id_to_idx,
768
+ valve_id_to_idx=sensor_config.valve_id_to_idx,
769
+ pump_id_to_idx=sensor_config.pump_id_to_idx,
770
+ tank_id_to_idx=sensor_config.tank_id_to_idx,
771
+ bulkspecies_id_to_idx=sensor_config.bulkspecies_id_to_idx,
772
+ surfacespecies_id_to_idx=sensor_config.surfacespecies_id_to_idx)
773
+
774
+ @property
775
+ def node_id_to_idx(self) -> dict:
776
+ """
777
+ Mapping of a surface node ID to the EPANET index
778
+ (i.e. position in the raw sensor reading data).
779
+
780
+ If None, it is assumed that the nodes (in 'nodes') are
781
+ sorted according to their EPANET index.
782
+
783
+ Returns
784
+ -------
785
+ `dict`
786
+ Node ID to index mapping.
787
+ """
788
+ return self.__node_id_to_idx
789
+
790
+ @property
791
+ def link_id_to_idx(self) -> dict:
792
+ """
793
+ Mapping of a link/pipe ID to the EPANET index
794
+ (i.e. position in the raw sensor reading data).
795
+
796
+ If None is given, it is assumed that the links/pipes (in 'links') are
797
+ sorted according to their EPANET index.
798
+
799
+ Returns
800
+ -------
801
+ `dict`
802
+ Link/Pipe ID to index mapping.
803
+ """
804
+ return self.__link_id_to_idx
805
+
806
+ @property
807
+ def valve_id_to_idx(self) -> dict:
808
+ """
809
+ Mapping of a valve ID to the EPANET index
810
+ (i.e. position in the raw sensor reading data).
811
+
812
+ If None, it is assumed that the valves (in 'valves') are
813
+ sorted according to their EPANET index.
814
+
815
+ Returns
816
+ -------
817
+ `dict`
818
+ Valve ID to index mapping.
819
+ """
820
+ return self.__valve_id_to_idx
821
+
822
+ @property
823
+ def pump_id_to_idx(self) -> dict:
824
+ """
825
+ Mapping of a pump ID to the EPANET index
826
+ (i.e. position in the raw sensor reading data).
827
+
828
+ If None, it is assumed that the pumps (in 'pumps') are
829
+ sorted according to their EPANET index.
830
+
831
+ Returns
832
+ -------
833
+ `dict`
834
+ Pump ID to index mapping.
835
+ """
836
+ return self.__pump_id_to_idx
837
+
838
+ @property
839
+ def tank_id_to_idx(self) -> dict:
840
+ """
841
+ Mapping of a tank ID to the EPANET index
842
+ (i.e. position in the raw sensor reading data).
843
+
844
+ If None, it is assumed that the tanks (in 'tanks') are
845
+ sorted according to their EPANET index.
846
+
847
+ Returns
848
+ -------
849
+ `dict`
850
+ Tank ID to index mapping.
851
+ """
852
+ return self.__tank_id_to_idx
853
+
854
+ @property
855
+ def bulkspecies_id_to_idx(self) -> dict:
856
+ """
857
+ Mapping of a bulk species ID to the EPANET index
858
+ (i.e. position in the raw sensor reading data).
859
+
860
+ If None, it is assumed that the bulk species (in 'bulk_species') are
861
+ sorted according to their EPANET index.
862
+
863
+ Returns
864
+ -------
865
+ `dict`
866
+ Bulk species ID to index mapping.
867
+ """
868
+ return self.__bulkspecies_id_to_idx
869
+
870
+ @property
871
+ def surfacespecies_id_to_idx(self) -> dict:
872
+ """
873
+ Mapping of a surface species ID to the EPANET index
874
+ (i.e. position in the raw sensor reading data).
875
+
876
+ If None, it is assumed that the surface species (in 'surface_species') are
877
+ sorted according to their EPANET index.
878
+
879
+ Returns
880
+ -------
881
+ `dict`
882
+ Surface species ID to index mapping.
883
+ """
884
+ return self.__surfacespecies_id_to_idx
608
885
 
609
- def node_id_to_idx(self, node_id: str) -> int:
886
+ def map_node_id_to_idx(self, node_id: str) -> int:
610
887
  """
611
888
  Gets the index of a given node ID.
612
889
 
@@ -625,7 +902,7 @@ class SensorConfig(JsonSerializable):
625
902
  else:
626
903
  return self.__nodes.index(node_id)
627
904
 
628
- def link_id_to_idx(self, link_id: str) -> int:
905
+ def map_link_id_to_idx(self, link_id: str) -> int:
629
906
  """
630
907
  Gets the index of a given link ID.
631
908
 
@@ -644,7 +921,7 @@ class SensorConfig(JsonSerializable):
644
921
  else:
645
922
  return self.__links.index(link_id)
646
923
 
647
- def valve_id_to_idx(self, valve_id: str) -> int:
924
+ def map_valve_id_to_idx(self, valve_id: str) -> int:
648
925
  """
649
926
  Gets the index of a given valve ID.
650
927
 
@@ -663,7 +940,7 @@ class SensorConfig(JsonSerializable):
663
940
  else:
664
941
  return self.__valves.index(valve_id)
665
942
 
666
- def pump_id_to_idx(self, pump_id: str) -> int:
943
+ def map_pump_id_to_idx(self, pump_id: str) -> int:
667
944
  """
668
945
  Gets the index of a given pump ID.
669
946
 
@@ -682,7 +959,7 @@ class SensorConfig(JsonSerializable):
682
959
  else:
683
960
  return self.__pumps.index(pump_id)
684
961
 
685
- def tank_id_to_idx(self, tank_id: str) -> int:
962
+ def map_tank_id_to_idx(self, tank_id: str) -> int:
686
963
  """
687
964
  Gets the index of a given tank ID.
688
965
 
@@ -701,7 +978,7 @@ class SensorConfig(JsonSerializable):
701
978
  else:
702
979
  return self.__tanks.index(tank_id)
703
980
 
704
- def bulkspecies_id_to_idx(self, bulk_species_id: str) -> int:
981
+ def map_bulkspecies_id_to_idx(self, bulk_species_id: str) -> int:
705
982
  """
706
983
  Gets the index of a given bulk species ID.
707
984
 
@@ -720,7 +997,7 @@ class SensorConfig(JsonSerializable):
720
997
  else:
721
998
  return self.__bulk_species.index(bulk_species_id)
722
999
 
723
- def surfacespecies_id_to_idx(self, surface_species_id: str) -> int:
1000
+ def map_surfacespecies_id_to_idx(self, surface_species_id: str) -> int:
724
1001
  """
725
1002
  Gets the index of a given surface species ID.
726
1003
 
@@ -740,35 +1017,35 @@ class SensorConfig(JsonSerializable):
740
1017
  return self.__surface_species.index(surface_species_id)
741
1018
 
742
1019
  def __compute_indices(self):
743
- self.__pressure_idx = np.array([self.node_id_to_idx(n)
1020
+ self.__pressure_idx = np.array([self.map_node_id_to_idx(n)
744
1021
  for n in self.__pressure_sensors], dtype=np.int32)
745
- self.__flow_idx = np.array([self.link_id_to_idx(link)
1022
+ self.__flow_idx = np.array([self.map_link_id_to_idx(link)
746
1023
  for link in self.__flow_sensors], dtype=np.int32)
747
- self.__demand_idx = np.array([self.node_id_to_idx(n)
1024
+ self.__demand_idx = np.array([self.map_node_id_to_idx(n)
748
1025
  for n in self.__demand_sensors], dtype=np.int32)
749
- self.__quality_node_idx = np.array([self.node_id_to_idx(n)
1026
+ self.__quality_node_idx = np.array([self.map_node_id_to_idx(n)
750
1027
  for n in self.__quality_node_sensors], dtype=np.int32)
751
- self.__quality_link_idx = np.array([self.link_id_to_idx(link)
1028
+ self.__quality_link_idx = np.array([self.map_link_id_to_idx(link)
752
1029
  for link in self.__quality_link_sensors],
753
1030
  dtype=np.int32)
754
- self.__valve_state_idx = np.array([self.valve_id_to_idx(v)
1031
+ self.__valve_state_idx = np.array([self.map_valve_id_to_idx(v)
755
1032
  for v in self.__valve_state_sensors], dtype=np.int32)
756
- self.__pump_state_idx = np.array([self.pump_id_to_idx(p)
1033
+ self.__pump_state_idx = np.array([self.map_pump_id_to_idx(p)
757
1034
  for p in self.__pump_state_sensors], dtype=np.int32)
758
- self.__tank_volume_idx = np.array([self.tank_id_to_idx(t)
1035
+ self.__tank_volume_idx = np.array([self.map_tank_id_to_idx(t)
759
1036
  for t in self.__tank_volume_sensors], dtype=np.int32)
760
- self.__bulk_species_node_idx = np.array([(self.bulkspecies_id_to_idx(s),
761
- [self.node_id_to_idx(node_id)
1037
+ self.__bulk_species_node_idx = np.array([(self.map_bulkspecies_id_to_idx(s),
1038
+ [self.map_node_id_to_idx(node_id)
762
1039
  for node_id in self.__bulk_species_node_sensors[s]])
763
1040
  for s in self.__bulk_species_node_sensors.keys()],
764
1041
  dtype=object)
765
- self.__bulk_species_link_idx = np.array([(self.bulkspecies_id_to_idx(s),
766
- [self.link_id_to_idx(link_id)
1042
+ self.__bulk_species_link_idx = np.array([(self.map_bulkspecies_id_to_idx(s),
1043
+ [self.map_link_id_to_idx(link_id)
767
1044
  for link_id in self.__bulk_species_link_sensors[s]])
768
1045
  for s in self.__bulk_species_link_sensors.keys()],
769
1046
  dtype=object)
770
- self.__surface_species_idx = np.array([(self.surfacespecies_id_to_idx(s),
771
- [self.link_id_to_idx(link_id)
1047
+ self.__surface_species_idx = np.array([(self.map_surfacespecies_id_to_idx(s),
1048
+ [self.map_link_id_to_idx(link_id)
772
1049
  for link_id in self.__surface_species_sensors[s]])
773
1050
  for s in self.__surface_species_sensors.keys()],
774
1051
  dtype=object)
@@ -781,8 +1058,10 @@ class SensorConfig(JsonSerializable):
781
1058
  n_valve_state_sensors = len(self.__valve_state_sensors)
782
1059
  n_pump_state_sensors = len(self.__pump_state_sensors)
783
1060
  n_tank_volume_sensors = len(self.__tank_volume_sensors)
784
- n_bulk_species_node_sensors = len(self.__bulk_species_node_sensors.values())
785
- n_bulk_species_link_sensors = len(self.__bulk_species_link_sensors.values())
1061
+ n_bulk_species_node_sensors = len(list(itertools.chain(
1062
+ *self.__bulk_species_node_sensors.values())))
1063
+ n_bulk_species_link_sensors = len(list(itertools.chain(
1064
+ *self.__bulk_species_link_sensors.values())))
786
1065
 
787
1066
  pressure_idx_shift = 0
788
1067
  flow_idx_shift = pressure_idx_shift + n_pressure_sensors
@@ -956,7 +1235,7 @@ class SensorConfig(JsonSerializable):
956
1235
 
957
1236
  Will be one of the following EPANET toolkit constants:
958
1237
 
959
- - EN_CFS = 0 (cu foot/sec)
1238
+ - EN_CFS = 0 (cubic foot/sec)
960
1239
  - EN_GPM = 1 (gal/min)
961
1240
  - EN_MGD = 2 (Million gal/day)
962
1241
  - EN_IMGD = 3 (Imperial MGD)
@@ -1319,7 +1598,8 @@ class SensorConfig(JsonSerializable):
1319
1598
  f"but not of '{type(bulk_species_sensors)}'")
1320
1599
  if any(species_id not in self.__bulk_species for species_id in bulk_species_sensors.keys()):
1321
1600
  raise ValueError("Unknown bulk species ID in 'bulk_species_sensors'")
1322
- if any(link_id not in self.__links for link_id in sum(bulk_species_sensors.values(), [])):
1601
+ if any(link_id not in self.__links for link_id in list(itertools.chain(
1602
+ *bulk_species_sensors.values()))):
1323
1603
  raise ValueError("Unknown link/pipe ID in 'bulk_species_sensors'")
1324
1604
 
1325
1605
  self.__bulk_species_link_sensors = bulk_species_sensors
@@ -1348,7 +1628,7 @@ class SensorConfig(JsonSerializable):
1348
1628
  for species_id in surface_species_sensors.keys()):
1349
1629
  raise ValueError("Unknown surface species ID in 'surface_species_sensors'")
1350
1630
  if any(link_id not in self.__links
1351
- for link_id in sum(surface_species_sensors.values(), [])):
1631
+ for link_id in list(itertools.chain(*surface_species_sensors.values()))):
1352
1632
  raise ValueError("Unknown link/pipe ID in 'surface_species_sensors'")
1353
1633
 
1354
1634
  self.__surface_species_sensors = surface_species_sensors
@@ -1422,12 +1702,24 @@ class SensorConfig(JsonSerializable):
1422
1702
  and self.__quality_unit == other.quality_unit \
1423
1703
  and self.__bulk_species_mass_unit == other.bulk_species_mass_unit \
1424
1704
  and self.__surface_species_mass_unit == other.surface_species_mass_unit \
1425
- and self.__surface_species_area_unit == other.surface_species_area_unit
1705
+ and self.__surface_species_area_unit == other.surface_species_area_unit \
1706
+ and self.__node_id_to_idx == other.node_id_to_idx \
1707
+ and self.__link_id_to_idx == other.link_id_to_idx \
1708
+ and self.__valve_id_to_idx == other.valve_id_to_idx \
1709
+ and self.__pump_id_to_idx == other.pump_id_to_idx \
1710
+ and self.__tank_id_to_idx == other.tank_id_to_idx \
1711
+ and self.__bulkspecies_id_to_idx == other.bulkspecies_id_to_idx \
1712
+ and self.__surfacespecies_id_to_idx == other.surfacespecies_id_to_idx
1426
1713
 
1427
1714
  def __str__(self) -> str:
1428
1715
  return f"nodes: {self.__nodes} links: {self.__links} valves: {self.__valves} " +\
1429
1716
  f"pumps: {self.__pumps} tanks: {self.__tanks} bulk_species: {self.__bulk_species} " +\
1430
- f"surface_species: {self.__surface_species}" + \
1717
+ f"surface_species: {self.__surface_species} " + \
1718
+ f"node_id_to_idx: {self.__node_id_to_idx} link_id_to_idx: {self.__link_id_to_idx} " +\
1719
+ f"pump_id_to_idx: {self.__pump_id_to_idx} tank_id_to_idx: {self.__tank_id_to_idx} " +\
1720
+ f"valve_id_to_idx: {self.__valve_id_to_idx} " +\
1721
+ f"bulkspecies_id_to_idx: {self.__bulkspecies_id_to_idx} " +\
1722
+ f"surfacespecies_id_to_idx: {self.__surfacespecies_id_to_idx}" +\
1431
1723
  f"pressure_sensors: {self.__pressure_sensors} flow_sensors: {self.__flow_sensors} " +\
1432
1724
  f"demand_sensors: {self.__demand_sensors} " +\
1433
1725
  f"quality_node_sensors: {self.__quality_node_sensors} " +\
@@ -1438,10 +1730,13 @@ class SensorConfig(JsonSerializable):
1438
1730
  f"bulk_species_node_sensors: {self.__bulk_species_node_sensors} " +\
1439
1731
  f"bulk_species_link_sensors: {self.__bulk_species_link_sensors} " +\
1440
1732
  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}"
1733
+ f"flow_unit: {flowunit_to_str(self.__flow_unit)} " +\
1734
+ f"quality_unit: {qualityunit_to_str(self.__quality_unit)} " +\
1735
+ "bulk_species_mass_unit: " +\
1736
+ f"{list(map(massunit_to_str, self.__bulk_species_mass_unit))} " +\
1737
+ "surface_species_mass_unit: " +\
1738
+ f"{list(map(massunit_to_str, self.__surface_species_mass_unit))} " +\
1739
+ f"surface_species_area_unit: {areaunit_to_str(self.__surface_species_area_unit)}"
1445
1740
 
1446
1741
  def compute_readings(self, pressures: np.ndarray, flows: np.ndarray, demands: np.ndarray,
1447
1742
  nodes_quality: np.ndarray, links_quality: np.ndarray,