epyt-flow 0.2.0__py3-none-any.whl → 0.3.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.
- epyt_flow/EPANET/compile_macos.sh +4 -0
- epyt_flow/VERSION +1 -1
- epyt_flow/__init__.py +6 -2
- epyt_flow/data/benchmarks/leakdb.py +7 -12
- epyt_flow/data/networks.py +404 -40
- epyt_flow/rest_api/base_handler.py +14 -0
- epyt_flow/rest_api/scada_data/handlers.py +42 -0
- epyt_flow/rest_api/server.py +3 -1
- epyt_flow/simulation/events/leakages.py +27 -17
- epyt_flow/simulation/scada/scada_data.py +543 -12
- epyt_flow/simulation/scada/scada_data_export.py +38 -5
- epyt_flow/simulation/scenario_config.py +2 -2
- epyt_flow/simulation/scenario_simulator.py +44 -35
- epyt_flow/simulation/sensor_config.py +342 -47
- epyt_flow/topology.py +104 -4
- {epyt_flow-0.2.0.dist-info → epyt_flow-0.3.0.dist-info}/METADATA +17 -6
- {epyt_flow-0.2.0.dist-info → epyt_flow-0.3.0.dist-info}/RECORD +20 -19
- {epyt_flow-0.2.0.dist-info → epyt_flow-0.3.0.dist-info}/LICENSE +0 -0
- {epyt_flow-0.2.0.dist-info → epyt_flow-0.3.0.dist-info}/WHEEL +0 -0
- {epyt_flow-0.2.0.dist-info → epyt_flow-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
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
|
|
91
|
-
- MASS_UNIT_UG
|
|
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
|
|
106
|
+
def massunit_to_str(unit_id: int) -> str:
|
|
105
107
|
"""
|
|
106
|
-
Converts a given
|
|
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
|
|
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
|
|
116
|
-
- MASS_UNIT_UG
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
761
|
-
[self.
|
|
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.
|
|
766
|
-
[self.
|
|
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.
|
|
771
|
-
[self.
|
|
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(
|
|
785
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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}
|
|
1442
|
-
f"
|
|
1443
|
-
|
|
1444
|
-
f"
|
|
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,
|