epyt-flow 0.4.0__py3-none-any.whl → 0.6.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/VERSION +1 -1
- epyt_flow/data/benchmarks/battledim.py +6 -2
- epyt_flow/data/benchmarks/leakdb.py +11 -9
- epyt_flow/data/networks.py +1 -2
- epyt_flow/gym/scenario_control_env.py +101 -6
- epyt_flow/serialization.py +13 -3
- epyt_flow/simulation/scada/advanced_control.py +1 -1
- epyt_flow/simulation/scada/scada_data.py +159 -55
- epyt_flow/simulation/scenario_config.py +6 -2
- epyt_flow/simulation/scenario_simulator.py +274 -64
- epyt_flow/simulation/scenario_visualizer.py +1 -1
- epyt_flow/simulation/sensor_config.py +222 -15
- epyt_flow/topology.py +162 -4
- epyt_flow/utils.py +25 -2
- {epyt_flow-0.4.0.dist-info → epyt_flow-0.6.0.dist-info}/METADATA +2 -2
- {epyt_flow-0.4.0.dist-info → epyt_flow-0.6.0.dist-info}/RECORD +19 -19
- {epyt_flow-0.4.0.dist-info → epyt_flow-0.6.0.dist-info}/WHEEL +1 -1
- {epyt_flow-0.4.0.dist-info → epyt_flow-0.6.0.dist-info}/LICENSE +0 -0
- {epyt_flow-0.4.0.dist-info → epyt_flow-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@ Module provides a class for specifying scenario configurations.
|
|
|
3
3
|
"""
|
|
4
4
|
from typing import Any
|
|
5
5
|
from copy import deepcopy
|
|
6
|
+
import os
|
|
6
7
|
import json
|
|
7
8
|
import numpy as np
|
|
8
9
|
|
|
@@ -219,7 +220,7 @@ class ScenarioConfig(Serializable):
|
|
|
219
220
|
`str`
|
|
220
221
|
Path to the .inp file.
|
|
221
222
|
"""
|
|
222
|
-
return self.__f_inp_in
|
|
223
|
+
return os.path.join(self._parent_path, self.__f_inp_in)
|
|
223
224
|
|
|
224
225
|
@property
|
|
225
226
|
def f_msx_in(self) -> str:
|
|
@@ -231,7 +232,10 @@ class ScenarioConfig(Serializable):
|
|
|
231
232
|
`str`
|
|
232
233
|
Path to the .msx file.
|
|
233
234
|
"""
|
|
234
|
-
|
|
235
|
+
if self.__f_msx_in is None:
|
|
236
|
+
return None
|
|
237
|
+
else:
|
|
238
|
+
return os.path.join(self._parent_path, self.__f_msx_in)
|
|
235
239
|
|
|
236
240
|
@property
|
|
237
241
|
def general_params(self) -> dict:
|
|
@@ -4,6 +4,7 @@ Module provides a class for scenario simulations.
|
|
|
4
4
|
import sys
|
|
5
5
|
import os
|
|
6
6
|
import pathlib
|
|
7
|
+
import time
|
|
7
8
|
from typing import Generator, Union
|
|
8
9
|
from copy import deepcopy
|
|
9
10
|
import shutil
|
|
@@ -21,8 +22,9 @@ from .sensor_config import SensorConfig, areaunit_to_id, massunit_to_id, quality
|
|
|
21
22
|
qualityunit_to_str, MASS_UNIT_MG, \
|
|
22
23
|
SENSOR_TYPE_LINK_FLOW, SENSOR_TYPE_LINK_QUALITY, SENSOR_TYPE_NODE_DEMAND, \
|
|
23
24
|
SENSOR_TYPE_NODE_PRESSURE, SENSOR_TYPE_NODE_QUALITY, \
|
|
24
|
-
SENSOR_TYPE_PUMP_STATE,
|
|
25
|
-
|
|
25
|
+
SENSOR_TYPE_PUMP_STATE, SENSOR_TYPE_PUMP_EFFICIENCY, SENSOR_TYPE_PUMP_ENERGYCONSUMPTION, \
|
|
26
|
+
SENSOR_TYPE_TANK_VOLUME, SENSOR_TYPE_VALVE_STATE, SENSOR_TYPE_NODE_BULK_SPECIES, \
|
|
27
|
+
SENSOR_TYPE_LINK_BULK_SPECIES, SENSOR_TYPE_SURFACE_SPECIES
|
|
26
28
|
from ..uncertainty import ModelUncertainty, SensorNoise
|
|
27
29
|
from .events import SystemEvent, Leakage, ActuatorEvent, SensorFault, SensorReadingAttack, \
|
|
28
30
|
SensorReadingEvent
|
|
@@ -109,12 +111,49 @@ class ScenarioSimulator():
|
|
|
109
111
|
if os.path.isfile(os.path.join(path_to_custom_libs, libepanetmsx_name)):
|
|
110
112
|
custom_epanetmsx_lib = os.path.join(path_to_custom_libs, libepanetmsx_name)
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
with warnings.catch_warnings():
|
|
115
|
+
# Treat all warnings as exceptions when trying to load .inp and .msx files
|
|
116
|
+
warnings.simplefilter('error')
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
# Workaround for EPyT bug concerning parallel simulations (see EPyT issue #54):
|
|
119
|
+
# 1. Create random tmp folder (make sure it is unique!)
|
|
120
|
+
# 2. Copy .inp and .msx file there
|
|
121
|
+
# 3. Use those copies when loading EPyT
|
|
122
|
+
tmp_folder_path = os.path.join(get_temp_folder(), f"{random.randint(int(1e5), int(1e7))}{time.time()}")
|
|
123
|
+
pathlib.Path(tmp_folder_path).mkdir(parents=True, exist_ok=False)
|
|
124
|
+
|
|
125
|
+
def __file_exists(file_in: str) -> bool:
|
|
126
|
+
try:
|
|
127
|
+
return pathlib.Path(file_in).is_file()
|
|
128
|
+
except Exception:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
if not __file_exists(self.__f_inp_in):
|
|
132
|
+
my_f_inp_in = self.__f_inp_in
|
|
133
|
+
self.__my_f_inp_in = None
|
|
134
|
+
else:
|
|
135
|
+
my_f_inp_in = os.path.join(tmp_folder_path, pathlib.Path(self.__f_inp_in).name)
|
|
136
|
+
shutil.copyfile(self.__f_inp_in, my_f_inp_in)
|
|
137
|
+
self.__my_f_inp_in = my_f_inp_in
|
|
138
|
+
|
|
139
|
+
if self.__f_msx_in is not None:
|
|
140
|
+
if not __file_exists(self.__f_msx_in):
|
|
141
|
+
my_f_msx_in = self.__f_msx_in
|
|
142
|
+
self.__my_f_msx_in = None
|
|
143
|
+
else:
|
|
144
|
+
my_f_msx_in = os.path.join(tmp_folder_path, pathlib.Path(self.__f_msx_in).name)
|
|
145
|
+
shutil.copyfile(self.__f_msx_in, my_f_msx_in)
|
|
146
|
+
self.__my_f_msx_in = my_f_msx_in
|
|
147
|
+
else:
|
|
148
|
+
my_f_msx_in = None
|
|
149
|
+
self.__my_f_msx_in = None
|
|
150
|
+
|
|
151
|
+
self.epanet_api = epanet(my_f_inp_in, ph=self.__f_msx_in is None,
|
|
152
|
+
customlib=custom_epanet_lib, loadfile=True,
|
|
153
|
+
display_msg=epanet_verbose)
|
|
154
|
+
|
|
155
|
+
if self.__f_msx_in is not None:
|
|
156
|
+
self.epanet_api.loadMSXFile(my_f_msx_in, customMSXlib=custom_epanetmsx_lib)
|
|
118
157
|
|
|
119
158
|
self.__sensor_config = self.__get_empty_sensor_config()
|
|
120
159
|
if scenario_config is not None:
|
|
@@ -393,6 +432,9 @@ class ScenarioSimulator():
|
|
|
393
432
|
new_sensor_config.quality_node_sensors = self.__sensor_config.quality_node_sensors
|
|
394
433
|
new_sensor_config.quality_link_sensors = self.__sensor_config.quality_link_sensors
|
|
395
434
|
new_sensor_config.pump_state_sensors = self.__sensor_config.pump_state_sensors
|
|
435
|
+
new_sensor_config.pump_efficiecy_sensors = self.__sensor_config.pump_efficiency_sensors
|
|
436
|
+
new_sensor_config.pump_energyconsumption_sensors = self.__sensor_config.\
|
|
437
|
+
pump_energyconsumption_sensors
|
|
396
438
|
new_sensor_config.valve_state_sensors = self.__sensor_config.valve_state_sensors
|
|
397
439
|
new_sensor_config.tank_volume_sensors = self.__sensor_config.tank_volume_sensors
|
|
398
440
|
new_sensor_config.bulk_species_node_sensors = self.__sensor_config.bulk_species_node_sensors
|
|
@@ -412,6 +454,11 @@ class ScenarioSimulator():
|
|
|
412
454
|
|
|
413
455
|
self.epanet_api.unload()
|
|
414
456
|
|
|
457
|
+
if self.__my_f_inp_in is not None:
|
|
458
|
+
shutil.rmtree(pathlib.Path(self.__my_f_inp_in).parent)
|
|
459
|
+
if self.__my_f_msx_in is not None:
|
|
460
|
+
shutil.rmtree(pathlib.Path(self.__my_f_msx_in).parent)
|
|
461
|
+
|
|
415
462
|
def __enter__(self):
|
|
416
463
|
return self
|
|
417
464
|
|
|
@@ -756,7 +803,7 @@ class ScenarioSimulator():
|
|
|
756
803
|
|
|
757
804
|
Returns
|
|
758
805
|
-------
|
|
759
|
-
|
|
806
|
+
:class:`~epyt_flow.topology.NetworkTopology`
|
|
760
807
|
Topology of this WDN as a graph.
|
|
761
808
|
"""
|
|
762
809
|
self.__adapt_to_network_changes()
|
|
@@ -1027,16 +1074,18 @@ class ScenarioSimulator():
|
|
|
1027
1074
|
----------
|
|
1028
1075
|
sensor_type : `int`
|
|
1029
1076
|
Sensor type. Must be one of the following:
|
|
1030
|
-
- SENSOR_TYPE_NODE_PRESSURE
|
|
1031
|
-
- SENSOR_TYPE_NODE_QUALITY
|
|
1032
|
-
- SENSOR_TYPE_NODE_DEMAND
|
|
1033
|
-
- SENSOR_TYPE_LINK_FLOW
|
|
1034
|
-
- SENSOR_TYPE_LINK_QUALITY
|
|
1035
|
-
- SENSOR_TYPE_VALVE_STATE
|
|
1036
|
-
- SENSOR_TYPE_PUMP_STATE
|
|
1037
|
-
- SENSOR_TYPE_TANK_VOLUME
|
|
1038
|
-
- SENSOR_TYPE_BULK_SPECIES
|
|
1039
|
-
- SENSOR_TYPE_SURFACE_SPECIES
|
|
1077
|
+
- SENSOR_TYPE_NODE_PRESSURE = 1
|
|
1078
|
+
- SENSOR_TYPE_NODE_QUALITY = 2
|
|
1079
|
+
- SENSOR_TYPE_NODE_DEMAND = 3
|
|
1080
|
+
- SENSOR_TYPE_LINK_FLOW = 4
|
|
1081
|
+
- SENSOR_TYPE_LINK_QUALITY = 5
|
|
1082
|
+
- SENSOR_TYPE_VALVE_STATE = 6
|
|
1083
|
+
- SENSOR_TYPE_PUMP_STATE = 7
|
|
1084
|
+
- SENSOR_TYPE_TANK_VOLUME = 8
|
|
1085
|
+
- SENSOR_TYPE_BULK_SPECIES = 9
|
|
1086
|
+
- SENSOR_TYPE_SURFACE_SPECIES = 10
|
|
1087
|
+
- SENSOR_TYPE_PUMP_EFFICIENCY = 12
|
|
1088
|
+
- SENSOR_TYPE_PUMP_ENERGYCONSUMPTION = 13
|
|
1040
1089
|
sensor_locations : `list[str]` or `dict`
|
|
1041
1090
|
Locations (IDs) of sensors either as a list or as a dict in the case of
|
|
1042
1091
|
bulk and surface species.
|
|
@@ -1057,6 +1106,10 @@ class ScenarioSimulator():
|
|
|
1057
1106
|
self.__sensor_config.valve_state_sensors = sensor_locations
|
|
1058
1107
|
elif sensor_type == SENSOR_TYPE_PUMP_STATE:
|
|
1059
1108
|
self.__sensor_config.pump_state_sensors = sensor_locations
|
|
1109
|
+
elif sensor_type == SENSOR_TYPE_PUMP_EFFICIENCY:
|
|
1110
|
+
self.__sensor_config.pump_efficiency_sensors = sensor_locations
|
|
1111
|
+
elif sensor_type == SENSOR_TYPE_PUMP_ENERGYCONSUMPTION:
|
|
1112
|
+
self.__sensor_config.pump_energyconsumption_sensors = sensor_locations
|
|
1060
1113
|
elif sensor_type == SENSOR_TYPE_TANK_VOLUME:
|
|
1061
1114
|
self.__sensor_config.tank_volume_sensors = sensor_locations
|
|
1062
1115
|
elif sensor_type == SENSOR_TYPE_NODE_BULK_SPECIES:
|
|
@@ -1081,6 +1134,12 @@ class ScenarioSimulator():
|
|
|
1081
1134
|
"""
|
|
1082
1135
|
self.set_sensors(SENSOR_TYPE_NODE_PRESSURE, sensor_locations)
|
|
1083
1136
|
|
|
1137
|
+
def place_pressure_sensors_everywhere(self) -> None:
|
|
1138
|
+
"""
|
|
1139
|
+
Places a pressure sensor at every node in the network.
|
|
1140
|
+
"""
|
|
1141
|
+
self.set_pressure_sensors(self.__sensor_config.nodes)
|
|
1142
|
+
|
|
1084
1143
|
def set_flow_sensors(self, sensor_locations: list[str]) -> None:
|
|
1085
1144
|
"""
|
|
1086
1145
|
Sets the flow sensors -- i.e. measuring flows at some links/pipes in the network.
|
|
@@ -1092,6 +1151,12 @@ class ScenarioSimulator():
|
|
|
1092
1151
|
"""
|
|
1093
1152
|
self.set_sensors(SENSOR_TYPE_LINK_FLOW, sensor_locations)
|
|
1094
1153
|
|
|
1154
|
+
def place_flow_sensors_everywhere(self) -> None:
|
|
1155
|
+
"""
|
|
1156
|
+
Places a flow sensors at every link/pipe in the network.
|
|
1157
|
+
"""
|
|
1158
|
+
self.set_flow_sensors(self.__sensor_config.links)
|
|
1159
|
+
|
|
1095
1160
|
def set_demand_sensors(self, sensor_locations: list[str]) -> None:
|
|
1096
1161
|
"""
|
|
1097
1162
|
Sets the demand sensors -- i.e. measuring demands at some nodes in the network.
|
|
@@ -1103,6 +1168,12 @@ class ScenarioSimulator():
|
|
|
1103
1168
|
"""
|
|
1104
1169
|
self.set_sensors(SENSOR_TYPE_NODE_DEMAND, sensor_locations)
|
|
1105
1170
|
|
|
1171
|
+
def place_demand_sensors_everywhere(self) -> None:
|
|
1172
|
+
"""
|
|
1173
|
+
Places a demand sensor at every node in the network.
|
|
1174
|
+
"""
|
|
1175
|
+
self.set_demand_sensors(self.__sensor_config.nodes)
|
|
1176
|
+
|
|
1106
1177
|
def set_node_quality_sensors(self, sensor_locations: list[str]) -> None:
|
|
1107
1178
|
"""
|
|
1108
1179
|
Sets the node quality sensors -- i.e. measuring the water quality
|
|
@@ -1115,6 +1186,12 @@ class ScenarioSimulator():
|
|
|
1115
1186
|
"""
|
|
1116
1187
|
self.set_sensors(SENSOR_TYPE_NODE_QUALITY, sensor_locations)
|
|
1117
1188
|
|
|
1189
|
+
def place_node_quality_sensors_everywhere(self) -> None:
|
|
1190
|
+
"""
|
|
1191
|
+
Places a water quality sensor at every node in the network.
|
|
1192
|
+
"""
|
|
1193
|
+
self.set_node_quality_sensors(self.__sensor_config.nodes)
|
|
1194
|
+
|
|
1118
1195
|
def set_link_quality_sensors(self, sensor_locations: list[str]) -> None:
|
|
1119
1196
|
"""
|
|
1120
1197
|
Sets the link quality sensors -- i.e. measuring the water quality
|
|
@@ -1127,6 +1204,12 @@ class ScenarioSimulator():
|
|
|
1127
1204
|
"""
|
|
1128
1205
|
self.set_sensors(SENSOR_TYPE_LINK_QUALITY, sensor_locations)
|
|
1129
1206
|
|
|
1207
|
+
def place_link_quality_sensors_everywhere(self) -> None:
|
|
1208
|
+
"""
|
|
1209
|
+
Places a water quality sensor at every link/pipe in the network.
|
|
1210
|
+
"""
|
|
1211
|
+
self.set_link_quality_sensors(self.__sensor_config.links)
|
|
1212
|
+
|
|
1130
1213
|
def set_valve_sensors(self, sensor_locations: list[str]) -> None:
|
|
1131
1214
|
"""
|
|
1132
1215
|
Sets the valve state sensors -- i.e. retrieving the state of some valves in the network.
|
|
@@ -1138,7 +1221,16 @@ class ScenarioSimulator():
|
|
|
1138
1221
|
"""
|
|
1139
1222
|
self.set_sensors(SENSOR_TYPE_VALVE_STATE, sensor_locations)
|
|
1140
1223
|
|
|
1141
|
-
def
|
|
1224
|
+
def place_valve_sensors_everywhere(self) -> None:
|
|
1225
|
+
"""
|
|
1226
|
+
Places a valve state sensor at every valve in the network.
|
|
1227
|
+
"""
|
|
1228
|
+
if len(self.__sensor_config.valves) == 0:
|
|
1229
|
+
warnings.warn("Network does not contain any valves", UserWarning)
|
|
1230
|
+
|
|
1231
|
+
self.set_valve_sensors(self.__sensor_config.valves)
|
|
1232
|
+
|
|
1233
|
+
def set_pump_state_sensors(self, sensor_locations: list[str]) -> None:
|
|
1142
1234
|
"""
|
|
1143
1235
|
Sets the pump state sensors -- i.e. retrieving the state of some pumps in the network.
|
|
1144
1236
|
|
|
@@ -1149,6 +1241,81 @@ class ScenarioSimulator():
|
|
|
1149
1241
|
"""
|
|
1150
1242
|
self.set_sensors(SENSOR_TYPE_PUMP_STATE, sensor_locations)
|
|
1151
1243
|
|
|
1244
|
+
def place_pump_state_sensors_everywhere(self) -> None:
|
|
1245
|
+
"""
|
|
1246
|
+
Places a pump state sensor at every pump in the network.
|
|
1247
|
+
"""
|
|
1248
|
+
if len(self.__sensor_config.pumps) == 0:
|
|
1249
|
+
warnings.warn("Network does not contain any pumps", UserWarning)
|
|
1250
|
+
|
|
1251
|
+
self.set_pump_state_sensors(self.__sensor_config.pumps)
|
|
1252
|
+
|
|
1253
|
+
def set_pump_efficiency_sensors(self, sensor_locations: list[str]) -> None:
|
|
1254
|
+
"""
|
|
1255
|
+
Sets the pump efficiency sensors -- i.e. retrieving the efficiency of
|
|
1256
|
+
some pumps in the network.
|
|
1257
|
+
|
|
1258
|
+
Parameters
|
|
1259
|
+
----------
|
|
1260
|
+
sensor_locations : `list[str]`
|
|
1261
|
+
Locations (IDs) of sensors.
|
|
1262
|
+
"""
|
|
1263
|
+
self.set_sensors(SENSOR_TYPE_PUMP_EFFICIENCY, sensor_locations)
|
|
1264
|
+
|
|
1265
|
+
def place_pump_efficiency_sensors_everywhere(self) -> None:
|
|
1266
|
+
"""
|
|
1267
|
+
Places a pump efficiency sensor at every pump in the network.
|
|
1268
|
+
"""
|
|
1269
|
+
if len(self.__sensor_config.pumps) == 0:
|
|
1270
|
+
warnings.warn("Network does not contain any pumps", UserWarning)
|
|
1271
|
+
|
|
1272
|
+
self.set_pump_efficiency_sensors(self.__sensor_config.pumps)
|
|
1273
|
+
|
|
1274
|
+
def set_pump_energyconsumption_sensors(self, sensor_locations: list[str]) -> None:
|
|
1275
|
+
"""
|
|
1276
|
+
Sets the pump energy consumption sensors -- i.e. retrieving the energy consumption of
|
|
1277
|
+
some pumps in the network.
|
|
1278
|
+
|
|
1279
|
+
Parameters
|
|
1280
|
+
----------
|
|
1281
|
+
sensor_locations : `list[str]`
|
|
1282
|
+
Locations (IDs) of sensors.
|
|
1283
|
+
"""
|
|
1284
|
+
self.set_sensors(SENSOR_TYPE_PUMP_ENERGYCONSUMPTION, sensor_locations)
|
|
1285
|
+
|
|
1286
|
+
def place_pump_energyconsumption_sensors_everywhere(self) -> None:
|
|
1287
|
+
"""
|
|
1288
|
+
Places a pump energy consumption sensor at every pump in the network.
|
|
1289
|
+
"""
|
|
1290
|
+
if len(self.__sensor_config.pumps) == 0:
|
|
1291
|
+
warnings.warn("Network does not contain any pumps", UserWarning)
|
|
1292
|
+
|
|
1293
|
+
self.set_pump_energyconsumption_sensors(self.__sensor_config.pumps)
|
|
1294
|
+
|
|
1295
|
+
def set_pump_sensors(self, sensor_locations: list[str]) -> None:
|
|
1296
|
+
"""
|
|
1297
|
+
Sets the pump sensors -- i.e. retrieving the state, efficiency, and energy consumption
|
|
1298
|
+
of some pumps in the network.
|
|
1299
|
+
|
|
1300
|
+
Parameters
|
|
1301
|
+
----------
|
|
1302
|
+
sensor_locations : `list[str]`
|
|
1303
|
+
Locations (IDs) of sensors.
|
|
1304
|
+
"""
|
|
1305
|
+
self.set_sensors(SENSOR_TYPE_PUMP_STATE, sensor_locations)
|
|
1306
|
+
self.set_sensors(SENSOR_TYPE_PUMP_EFFICIENCY, sensor_locations)
|
|
1307
|
+
self.set_sensors(SENSOR_TYPE_PUMP_ENERGYCONSUMPTION, sensor_locations)
|
|
1308
|
+
|
|
1309
|
+
def place_pump_sensors_everywhere(self) -> None:
|
|
1310
|
+
"""
|
|
1311
|
+
Palces pump sensors at every pump in the network -- i.e. retrieving the state, efficiency,
|
|
1312
|
+
and energy consumption of all pumps in the network.
|
|
1313
|
+
"""
|
|
1314
|
+
if len(self.__sensor_config.pumps) == 0:
|
|
1315
|
+
warnings.warn("Network does not contain any pumps", UserWarning)
|
|
1316
|
+
|
|
1317
|
+
self.set_pump_sensors(self.__sensor_config.pumps)
|
|
1318
|
+
|
|
1152
1319
|
def set_tank_sensors(self, sensor_locations: list[str]) -> None:
|
|
1153
1320
|
"""
|
|
1154
1321
|
Sets the tank volume sensors -- i.e. measuring water volumes in some tanks in the network.
|
|
@@ -1160,6 +1327,15 @@ class ScenarioSimulator():
|
|
|
1160
1327
|
"""
|
|
1161
1328
|
self.set_sensors(SENSOR_TYPE_TANK_VOLUME, sensor_locations)
|
|
1162
1329
|
|
|
1330
|
+
def place_tank_sensors_everywhere(self) -> None:
|
|
1331
|
+
"""
|
|
1332
|
+
Places a water tank volume sensor at every tank in the network.
|
|
1333
|
+
"""
|
|
1334
|
+
if len(self.__sensor_config.tanks) == 0:
|
|
1335
|
+
warnings.warn("Network does not contain any tanks", UserWarning)
|
|
1336
|
+
|
|
1337
|
+
self.set_tank_sensors(self.__sensor_config.tanks)
|
|
1338
|
+
|
|
1163
1339
|
def set_bulk_species_node_sensors(self, sensor_info: dict) -> None:
|
|
1164
1340
|
"""
|
|
1165
1341
|
Sets the bulk species node sensors -- i.e. measuring bulk species concentrations
|
|
@@ -1172,6 +1348,14 @@ class ScenarioSimulator():
|
|
|
1172
1348
|
"""
|
|
1173
1349
|
self.set_sensors(SENSOR_TYPE_NODE_BULK_SPECIES, sensor_info)
|
|
1174
1350
|
|
|
1351
|
+
def place_bulk_species_node_sensors_everywhere(self) -> None:
|
|
1352
|
+
"""
|
|
1353
|
+
Places bulk species concentration sensors at every node in the network for
|
|
1354
|
+
every bulk species.
|
|
1355
|
+
"""
|
|
1356
|
+
self.set_bulk_species_node_sensors({species_id: self.__sensor_config.nodes
|
|
1357
|
+
for species_id in self.__sensor_config.bulk_species})
|
|
1358
|
+
|
|
1175
1359
|
def set_bulk_species_link_sensors(self, sensor_info: dict) -> None:
|
|
1176
1360
|
"""
|
|
1177
1361
|
Sets the bulk species link/pipe sensors -- i.e. measuring bulk species concentrations
|
|
@@ -1184,6 +1368,14 @@ class ScenarioSimulator():
|
|
|
1184
1368
|
"""
|
|
1185
1369
|
self.set_sensors(SENSOR_TYPE_LINK_BULK_SPECIES, sensor_info)
|
|
1186
1370
|
|
|
1371
|
+
def place_bulk_species_link_sensors_everywhere(self) -> None:
|
|
1372
|
+
"""
|
|
1373
|
+
Places bulk species concentration sensors at every link/pipe in the network
|
|
1374
|
+
for every bulk species.
|
|
1375
|
+
"""
|
|
1376
|
+
self.set_bulk_species_link_sensors({species_id: self.__sensor_config.links
|
|
1377
|
+
for species_id in self.__sensor_config.bulk_species})
|
|
1378
|
+
|
|
1187
1379
|
def set_surface_species_sensors(self, sensor_info: dict) -> None:
|
|
1188
1380
|
"""
|
|
1189
1381
|
Sets the surface species sensors -- i.e. measuring surface species concentrations
|
|
@@ -1196,6 +1388,22 @@ class ScenarioSimulator():
|
|
|
1196
1388
|
"""
|
|
1197
1389
|
self.set_sensors(SENSOR_TYPE_SURFACE_SPECIES, sensor_info)
|
|
1198
1390
|
|
|
1391
|
+
def place_surface_species_sensors_everywhere(self) -> None:
|
|
1392
|
+
"""
|
|
1393
|
+
Places surface species concentration sensors at every link/pipe in the network
|
|
1394
|
+
for every surface species.
|
|
1395
|
+
"""
|
|
1396
|
+
self.set_bulk_species_node_sensors({species_id: self.__sensor_config.links
|
|
1397
|
+
for species_id in
|
|
1398
|
+
self.__sensor_config.surface_species})
|
|
1399
|
+
|
|
1400
|
+
def place_sensors_everywhere(self) -> None:
|
|
1401
|
+
"""
|
|
1402
|
+
Places sensors everywhere -- i.e. every possible quantity is monitored
|
|
1403
|
+
at every position in the network.
|
|
1404
|
+
"""
|
|
1405
|
+
self.__sensor_config.place_sensors_everywhere()
|
|
1406
|
+
|
|
1199
1407
|
def __prepare_simulation(self) -> None:
|
|
1200
1408
|
self.__adapt_to_network_changes()
|
|
1201
1409
|
|
|
@@ -1745,11 +1953,6 @@ class ScenarioSimulator():
|
|
|
1745
1953
|
tstep = 1
|
|
1746
1954
|
first_itr = True
|
|
1747
1955
|
while tstep > 0:
|
|
1748
|
-
if support_abort is True: # Can the simulation be aborted? If so, handle it.
|
|
1749
|
-
abort = yield
|
|
1750
|
-
if abort is not False:
|
|
1751
|
-
break
|
|
1752
|
-
|
|
1753
1956
|
if first_itr is True: # Fix current time in the first iteration
|
|
1754
1957
|
tstep = 0
|
|
1755
1958
|
first_itr = False
|
|
@@ -1781,8 +1984,8 @@ class ScenarioSimulator():
|
|
|
1781
1984
|
tanks_volume_data = self.epanet_api.getNodeTankVolume().reshape(1, -1)
|
|
1782
1985
|
|
|
1783
1986
|
pump_idx = self.epanet_api.getLinkPumpIndex()
|
|
1784
|
-
|
|
1785
|
-
|
|
1987
|
+
pumps_energy_usage_data = self.epanet_api.getLinkEnergy(pump_idx).reshape(1, -1)
|
|
1988
|
+
pumps_efficiency_data = self.epanet_api.getLinkPumpEfficiency().reshape(1, -1)
|
|
1786
1989
|
|
|
1787
1990
|
link_valve_idx = self.epanet_api.getLinkValveIndex()
|
|
1788
1991
|
valves_state_data = self.epanet_api.getLinkStatus(link_valve_idx).reshape(1, -1)
|
|
@@ -1796,8 +1999,8 @@ class ScenarioSimulator():
|
|
|
1796
1999
|
pumps_state_data_raw=pumps_state_data,
|
|
1797
2000
|
valves_state_data_raw=valves_state_data,
|
|
1798
2001
|
tanks_volume_data_raw=tanks_volume_data,
|
|
1799
|
-
|
|
1800
|
-
|
|
2002
|
+
pumps_energy_usage_data_raw=pumps_energy_usage_data,
|
|
2003
|
+
pumps_efficiency_data_raw=pumps_efficiency_data,
|
|
1801
2004
|
sensor_readings_time=np.array([total_time]),
|
|
1802
2005
|
sensor_reading_events=self.__sensor_reading_events,
|
|
1803
2006
|
sensor_noise=self.__sensor_noise,
|
|
@@ -1806,19 +2009,26 @@ class ScenarioSimulator():
|
|
|
1806
2009
|
# Yield results in a regular time interval only!
|
|
1807
2010
|
if total_time % reporting_time_step == 0 and total_time >= reporting_time_start:
|
|
1808
2011
|
if return_as_dict is True:
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
2012
|
+
data = {"pressure_data_raw": pressure_data,
|
|
2013
|
+
"flow_data_raw": flow_data,
|
|
2014
|
+
"demand_data_raw": demand_data,
|
|
2015
|
+
"node_quality_data_raw": quality_node_data,
|
|
2016
|
+
"link_quality_data_raw": quality_link_data,
|
|
2017
|
+
"pumps_state_data_raw": pumps_state_data,
|
|
2018
|
+
"valves_state_data_raw": valves_state_data,
|
|
2019
|
+
"tanks_volume_data_raw": tanks_volume_data,
|
|
2020
|
+
"pumps_energy_usage_data_raw": pumps_energy_usage_data,
|
|
2021
|
+
"pumps_efficiency_data_raw": pumps_efficiency_data,
|
|
2022
|
+
"sensor_readings_time": np.array([total_time])}
|
|
1820
2023
|
else:
|
|
1821
|
-
|
|
2024
|
+
data = scada_data
|
|
2025
|
+
|
|
2026
|
+
if support_abort is True: # Can the simulation be aborted? If so, handle it.
|
|
2027
|
+
abort = yield
|
|
2028
|
+
if abort is not False:
|
|
2029
|
+
break
|
|
2030
|
+
|
|
2031
|
+
yield data
|
|
1822
2032
|
|
|
1823
2033
|
# Apply control modules
|
|
1824
2034
|
for control in self.__controls:
|
|
@@ -1947,6 +2157,30 @@ class ScenarioSimulator():
|
|
|
1947
2157
|
"""
|
|
1948
2158
|
self.__adapt_to_network_changes()
|
|
1949
2159
|
|
|
2160
|
+
if flow_units_id is not None:
|
|
2161
|
+
if flow_units_id == ToolkitConstants.EN_CFS:
|
|
2162
|
+
self.epanet_api.setFlowUnitsCFS()
|
|
2163
|
+
elif flow_units_id == ToolkitConstants.EN_GPM:
|
|
2164
|
+
self.epanet_api.setFlowUnitsGPM()
|
|
2165
|
+
elif flow_units_id == ToolkitConstants.EN_MGD:
|
|
2166
|
+
self.epanet_api.setFlowUnitsMGD()
|
|
2167
|
+
elif flow_units_id == ToolkitConstants.EN_IMGD:
|
|
2168
|
+
self.epanet_api.setFlowUnitsIMGD()
|
|
2169
|
+
elif flow_units_id == ToolkitConstants.EN_AFD:
|
|
2170
|
+
self.epanet_api.setFlowUnitsAFD()
|
|
2171
|
+
elif flow_units_id == ToolkitConstants.EN_LPS:
|
|
2172
|
+
self.epanet_api.setFlowUnitsLPS()
|
|
2173
|
+
elif flow_units_id == ToolkitConstants.EN_LPM:
|
|
2174
|
+
self.epanet_api.setFlowUnitsLPM()
|
|
2175
|
+
elif flow_units_id == ToolkitConstants.EN_MLD:
|
|
2176
|
+
self.epanet_api.setFlowUnitsMLD()
|
|
2177
|
+
elif flow_units_id == ToolkitConstants.EN_CMH:
|
|
2178
|
+
self.epanet_api.setFlowUnitsCMH()
|
|
2179
|
+
elif flow_units_id == ToolkitConstants.EN_CMD:
|
|
2180
|
+
self.epanet_api.setFlowUnitsCMD()
|
|
2181
|
+
else:
|
|
2182
|
+
raise ValueError(f"Unknown flow units '{flow_units_id}'")
|
|
2183
|
+
|
|
1950
2184
|
if demand_model is not None:
|
|
1951
2185
|
self.epanet_api.setDemandModel(demand_model["type"], demand_model["pressure_min"],
|
|
1952
2186
|
demand_model["pressure_required"],
|
|
@@ -1991,30 +2225,6 @@ class ScenarioSimulator():
|
|
|
1991
2225
|
"greater than the hydraulic time step")
|
|
1992
2226
|
self.epanet_api.setTimeQualityStep(quality_time_step)
|
|
1993
2227
|
|
|
1994
|
-
if flow_units_id is not None:
|
|
1995
|
-
if flow_units_id == ToolkitConstants.EN_CFS:
|
|
1996
|
-
self.epanet_api.setFlowUnitsCFS()
|
|
1997
|
-
elif flow_units_id == ToolkitConstants.EN_GPM:
|
|
1998
|
-
self.epanet_api.setFlowUnitsGPM()
|
|
1999
|
-
elif flow_units_id == ToolkitConstants.EN_MGD:
|
|
2000
|
-
self.epanet_api.setFlowUnitsMGD()
|
|
2001
|
-
elif flow_units_id == ToolkitConstants.EN_IMGD:
|
|
2002
|
-
self.epanet_api.setFlowUnitsIMGD()
|
|
2003
|
-
elif flow_units_id == ToolkitConstants.EN_AFD:
|
|
2004
|
-
self.epanet_api.setFlowUnitsAFD()
|
|
2005
|
-
elif flow_units_id == ToolkitConstants.EN_LPS:
|
|
2006
|
-
self.epanet_api.setFlowUnitsLPS()
|
|
2007
|
-
elif flow_units_id == ToolkitConstants.EN_LPM:
|
|
2008
|
-
self.epanet_api.setFlowUnitsLPM()
|
|
2009
|
-
elif flow_units_id == ToolkitConstants.EN_MLD:
|
|
2010
|
-
self.epanet_api.setFlowUnitsMLD()
|
|
2011
|
-
elif flow_units_id == ToolkitConstants.EN_CMH:
|
|
2012
|
-
self.epanet_api.setFlowUnitsCMH()
|
|
2013
|
-
elif flow_units_id == ToolkitConstants.EN_CMD:
|
|
2014
|
-
self.epanet_api.setFlowUnitsCMD()
|
|
2015
|
-
else:
|
|
2016
|
-
raise ValueError(f"Unknown flow units '{flow_units_id}'")
|
|
2017
|
-
|
|
2018
2228
|
if quality_model is not None:
|
|
2019
2229
|
if quality_model["type"] == "NONE":
|
|
2020
2230
|
self.epanet_api.setQualityType("none")
|
|
@@ -12,7 +12,7 @@ class ScenarioVisualizer():
|
|
|
12
12
|
|
|
13
13
|
Parameters
|
|
14
14
|
----------
|
|
15
|
-
scenario : :class
|
|
15
|
+
scenario : :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
|
|
16
16
|
Scenario to be visualized.
|
|
17
17
|
"""
|
|
18
18
|
def __init__(self, scenario: ScenarioSimulator):
|