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.
@@ -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
- return self.__f_msx_in
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, SENSOR_TYPE_TANK_VOLUME, SENSOR_TYPE_VALVE_STATE, \
25
- SENSOR_TYPE_NODE_BULK_SPECIES, SENSOR_TYPE_LINK_BULK_SPECIES, SENSOR_TYPE_SURFACE_SPECIES
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
- self.epanet_api = epanet(self.__f_inp_in, ph=self.__f_msx_in is None,
113
- customlib=custom_epanet_lib, loadfile=True,
114
- display_msg=epanet_verbose)
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
- if self.__f_msx_in is not None:
117
- self.epanet_api.loadMSXFile(self.__f_msx_in, customMSXlib=custom_epanetmsx_lib)
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
- `epyt_flow.topology.NetworkTopology`
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 = 1
1031
- - SENSOR_TYPE_NODE_QUALITY = 2
1032
- - SENSOR_TYPE_NODE_DEMAND = 3
1033
- - SENSOR_TYPE_LINK_FLOW = 4
1034
- - SENSOR_TYPE_LINK_QUALITY = 5
1035
- - SENSOR_TYPE_VALVE_STATE = 6
1036
- - SENSOR_TYPE_PUMP_STATE = 7
1037
- - SENSOR_TYPE_TANK_VOLUME = 8
1038
- - SENSOR_TYPE_BULK_SPECIES = 9
1039
- - SENSOR_TYPE_SURFACE_SPECIES = 10
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 set_pump_sensors(self, sensor_locations: list[str]) -> None:
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
- pump_energy_usage_data = self.epanet_api.getLinkEnergy(pump_idx).reshape(1, -1)
1785
- pump_efficiency_data = self.epanet_api.getLinkPumpEfficiency().reshape(1, -1)
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
- pump_energy_usage_data=pump_energy_usage_data,
1800
- pump_efficiency_data=pump_efficiency_data,
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
- yield {"pressure_data_raw": pressure_data,
1810
- "flow_data_raw": flow_data,
1811
- "demand_data_raw": demand_data,
1812
- "node_quality_data_raw": quality_node_data,
1813
- "link_quality_data_raw": quality_link_data,
1814
- "pumps_state_data_raw": pumps_state_data,
1815
- "valves_state_data_raw": valves_state_data,
1816
- "tanks_volume_data_raw": tanks_volume_data,
1817
- "pump_energy_usage_data": pump_energy_usage_data,
1818
- "pump_efficiency_data": pump_efficiency_data,
1819
- "sensor_readings_time": np.array([total_time])}
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
- yield scada_data
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:`epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
15
+ scenario : :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
16
16
  Scenario to be visualized.
17
17
  """
18
18
  def __init__(self, scenario: ScenarioSimulator):