epyt-flow 0.3.0__py3-none-any.whl → 0.5.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 CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.5.0
@@ -543,7 +543,7 @@ def load_scenarios(scenarios_id: list[int], use_net1: bool = True,
543
543
 
544
544
  my_uncertainties = {"pipe_length_uncertainty": MyUniformUncertainty(low=0, high=0.25),
545
545
  "pipe_roughness_uncertainty": MyUniformUncertainty(low=0, high=0.25),
546
- "demand_base_uncertainty": MyUniformUncertainty(low=0, high=0.25)}
546
+ "base_demand_uncertainty": MyUniformUncertainty(low=0, high=0.25)}
547
547
  model_uncertainty = ModelUncertainty(**my_uncertainties)
548
548
 
549
549
  # Create sensor config (place pressure and flow sensors everywhere)
@@ -182,7 +182,7 @@ class Leakage(SystemEvent, JsonSerializable):
182
182
  raise TypeError(f"Can not compare 'Leakage' instance with '{type(other)}' instance")
183
183
 
184
184
  return super().__eq__(other) and self.__link_id == other.link_id \
185
- and self.__diameter == other.diameter and self.__profile == other.profile \
185
+ and self.__diameter == other.diameter and np.all(self.__profile == other.profile) \
186
186
  and self.__node_id == other.node_id and self.area == other.area
187
187
 
188
188
  def __str__(self) -> str:
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Module provides functions for simulating several scenarios in parallel.
3
3
  """
4
- from typing import Callable
4
+ from typing import Callable, Any
5
5
  import os
6
6
  import warnings
7
7
  from multiprocess import Pool, cpu_count
@@ -38,9 +38,9 @@ def callback_save_to_file(folder_out: str = "") -> Callable[[ScadaData, Scenario
38
38
 
39
39
 
40
40
  def _run_scenario_simulation(scenario_config: ScenarioConfig, scenario_idx: int,
41
- callback: Callable[[ScadaData, ScenarioConfig, int], None]) -> None:
41
+ callback: Callable[[ScadaData, ScenarioConfig, int], Any]) -> Any:
42
42
  with ScenarioSimulator(scenario_config=scenario_config) as sim:
43
- callback(sim.run_simulation(), scenario_config, scenario_idx)
43
+ return callback(sim.run_simulation(), scenario_config, scenario_idx)
44
44
 
45
45
 
46
46
  class ParallelScenarioSimulation():
@@ -50,8 +50,8 @@ class ParallelScenarioSimulation():
50
50
  @staticmethod
51
51
  def run(scenarios: list[ScenarioConfig], n_jobs: int = -1,
52
52
  max_working_memory_consumption: int = None,
53
- callback: Callable[[ScadaData, ScenarioConfig, int], None] = callback_save_to_file()
54
- ) -> None:
53
+ callback: Callable[[ScadaData, ScenarioConfig, int], Any] = callback_save_to_file()
54
+ ) -> Any:
55
55
  """
56
56
  Simulates multiple scenarios in parallel.
57
57
 
@@ -76,7 +76,7 @@ class ParallelScenarioSimulation():
76
76
 
77
77
  The callback gets the simulation results as a
78
78
  :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` instance, the scenario
79
- configuration as a :class: `epyt_flow.simulation.scenario_config.ScenarioConfig`
79
+ configuration as a :class:`~epyt_flow.simulation.scenario_config.ScenarioConfig`
80
80
  instance, and the index of the scenario in 'scenarios' as arguments.
81
81
 
82
82
  The default is :func:`~epyt_flow.simulation.parallel_simulation.callback_save_to_file`.
@@ -144,4 +144,4 @@ class ParallelScenarioSimulation():
144
144
  scenarios_task.append((scenario, scenario_idx, callback))
145
145
 
146
146
  with Pool(processes=n_parallel_scenarios, maxtasksperchild=1) as pool:
147
- pool.starmap(_run_scenario_simulation, scenarios_task)
147
+ return pool.starmap(_run_scenario_simulation, scenarios_task)
@@ -1163,6 +1163,9 @@ class ScadaData(Serializable):
1163
1163
  :func:`~epyt_flow.simulation.scada.scada_data.ScadaData.get_data` --
1164
1164
  calling this function is the only way of accessing the energy usage of each pump.
1165
1165
 
1166
+ The odering in the returned NumPy array corresponds to the ordering
1167
+ of the pumps in EPANET.
1168
+
1166
1169
  Returns
1167
1170
  -------
1168
1171
  `numpy.ndarray`
@@ -1180,6 +1183,9 @@ class ScadaData(Serializable):
1180
1183
  :func:`~epyt_flow.simulation.scada.scada_data.ScadaData.get_data` --
1181
1184
  calling this function is the only way of accessing the pumps' efficiency.
1182
1185
 
1186
+ The odering in the returned NumPy array corresponds to the ordering
1187
+ of the pumps in EPANET.
1188
+
1183
1189
  Returns
1184
1190
  -------
1185
1191
  `numpy.ndarray`
@@ -109,7 +109,7 @@ class ScadaDataExport():
109
109
 
110
110
  def __get_sensor_unit(sensor_type):
111
111
  if sensor_type == "pressure":
112
- if is_flowunit_simetric(scada_data.sensor_config.flow_unit):
112
+ if not is_flowunit_simetric(scada_data.sensor_config.flow_unit):
113
113
  return "psi"
114
114
  else:
115
115
  return "meter"
@@ -4,6 +4,7 @@ Module provides a class for specifying scenario configurations.
4
4
  from typing import Any
5
5
  from copy import deepcopy
6
6
  import json
7
+ import numpy as np
7
8
 
8
9
  from ..uncertainty import AbsoluteGaussianUncertainty, RelativeGaussianUncertainty, \
9
10
  AbsoluteUniformUncertainty, RelativeUniformUncertainty, ModelUncertainty, \
@@ -350,10 +351,11 @@ class ScenarioConfig(Serializable):
350
351
  return self.__f_inp_in == other.f_inp_in and self.__f_msx_in == other.f_msx_in \
351
352
  and self.__general_params == other.general_params \
352
353
  and self.__memory_consumption_estimate == other.memory_consumption_estimate \
353
- and self.__sensor_config == other.sensor_config and self.__controls == other.controls \
354
+ and self.__sensor_config == other.sensor_config \
355
+ and np.all(self.__controls == other.controls) \
354
356
  and self.__model_uncertainty == other.model_uncertainty \
355
- and self.__system_events == other.system_events \
356
- and self.__sensor_reading_events == other.sensor_reading_events
357
+ and np.all(self.__system_events == other.system_events) \
358
+ and np.all(self.__sensor_reading_events == other.sensor_reading_events)
357
359
 
358
360
  def __str__(self) -> str:
359
361
  return f"f_inp_in: {self.f_inp_in} f_msx_in: {self.f_msx_in} " + \
@@ -298,6 +298,7 @@ class ScenarioSimulator():
298
298
 
299
299
  return deepcopy(list(filter(lambda e: isinstance(e, Leakage), self.__system_events)))
300
300
 
301
+ @property
301
302
  def actuator_events(self) -> list[ActuatorEvent]:
302
303
  """
303
304
  Gets all actuator events.
@@ -483,6 +484,7 @@ class ScenarioSimulator():
483
484
 
484
485
  if inp_file_path is not None:
485
486
  self.epanet_api.saveInputFile(inp_file_path)
487
+ self.__f_inp_in = inp_file_path
486
488
 
487
489
  if export_sensor_config is True:
488
490
  report_desc = "\n\n[REPORT]\n"
@@ -537,6 +539,7 @@ class ScenarioSimulator():
537
539
 
538
540
  if self.__f_msx_in is not None and msx_file_path is not None:
539
541
  self.epanet_api.saveMSXFile(msx_file_path)
542
+ self.__f_msx_in = msx_file_path
540
543
 
541
544
  if export_sensor_config is True:
542
545
  report_desc = "\n\n[REPORT]\n"
@@ -708,8 +711,9 @@ class ScenarioSimulator():
708
711
 
709
712
  general_params = {"hydraulic_time_step": self.get_hydraulic_time_step(),
710
713
  "quality_time_step": self.get_quality_time_step(),
714
+ "reporting_time_step": self.epanet_api.getTimeReportingStep(),
711
715
  "simulation_duration": self.get_simulation_duration(),
712
- "flow_units": self.get_flow_units(),
716
+ "flow_units_id": self.get_flow_units(),
713
717
  "quality_model": self.get_quality_model(),
714
718
  "demand_model": self.get_demand_model()}
715
719
 
@@ -766,6 +770,7 @@ class ScenarioSimulator():
766
770
  node_tank_names = self.epanet_api.getNodeTankNameID()
767
771
 
768
772
  links_id = self.epanet_api.getLinkNameID()
773
+ links_type = self.epanet_api.getLinkType()
769
774
  links_data = self.epanet_api.getNodesConnectingLinksID()
770
775
  links_diameter = self.epanet_api.getLinkDiameter()
771
776
  links_length = self.epanet_api.getLinkLength()
@@ -774,6 +779,11 @@ class ScenarioSimulator():
774
779
  links_wall_coeff = self.epanet_api.getLinkWallReactionCoeff()
775
780
  links_loss_coeff = self.epanet_api.getLinkMinorLossCoeff()
776
781
 
782
+ pumps_id = self.epanet_api.getLinkPumpNameID()
783
+ pumps_type = self.epanet_api.getLinkPumpType()
784
+
785
+ valves_id = self.epanet_api.getLinkValveNameID()
786
+
777
787
  # Build graph describing the topology
778
788
  nodes = []
779
789
  for node_id, node_elevation, node_type, node_coord in zip(nodes_id, nodes_elevation,
@@ -788,16 +798,30 @@ class ScenarioSimulator():
788
798
  nodes.append((node_id, node_info))
789
799
 
790
800
  links = []
791
- for link_id, link, diameter, length, roughness_coeff, bulk_coeff, wall_coeff, loss_coeff \
792
- in zip(links_id, links_data, links_diameter, links_length, links_roughness_coeff,
793
- links_bulk_coeff, links_wall_coeff, links_loss_coeff):
794
- links.append((link_id, link, {"diameter": diameter, "length": length,
795
- "roughness_coeff": roughness_coeff,
796
- "bulk_coeff": bulk_coeff, "wall_coeff": wall_coeff,
797
- "loss_coeff": loss_coeff}))
798
-
799
- return NetworkTopology(f_inp=self.f_inp_in, nodes=nodes, links=links,
800
- units=self.get_units_category())
801
+ for link_id, link_type, link, diameter, length, roughness_coeff, bulk_coeff, wall_coeff, loss_coeff \
802
+ in zip(links_id, links_type, links_data, links_diameter, links_length,
803
+ links_roughness_coeff, links_bulk_coeff, links_wall_coeff, links_loss_coeff):
804
+ links.append((link_id, list(link),
805
+ {"type": link_type, "diameter": diameter, "length": length,
806
+ "roughness_coeff": roughness_coeff,
807
+ "bulk_coeff": bulk_coeff, "wall_coeff": wall_coeff,
808
+ "loss_coeff": loss_coeff}))
809
+
810
+ pumps = {}
811
+ for pump_id, pump_type in zip(pumps_id, pumps_type):
812
+ link_idx = links_id.index(pump_id)
813
+ link = links_data[link_idx]
814
+ pumps[pump_id] = {"type": pump_type, "end_points": link}
815
+
816
+ valves = {}
817
+ for valve_id in valves_id:
818
+ link_idx = links_id.index(valve_id)
819
+ link = links_data[link_idx]
820
+ valve_type = links_type[link_idx]
821
+ valves[valve_id] = {"type": valve_type, "end_points": link}
822
+
823
+ return NetworkTopology(f_inp=self.f_inp_in, nodes=nodes, links=links, pumps=pumps,
824
+ valves=valves, units=self.get_units_category())
801
825
 
802
826
  def randomize_demands(self) -> None:
803
827
  """
@@ -1818,14 +1842,14 @@ class ScenarioSimulator():
1818
1842
 
1819
1843
  Parameters
1820
1844
  ----------
1821
- model_uncertainty : :class:`~epyt_flow.uncertainties.model_uncertainty.ModelUncertainty`
1845
+ model_uncertainty : :class:`~epyt_flow.uncertainty.model_uncertainty.ModelUncertainty`
1822
1846
  Model uncertainty specifications.
1823
1847
  """
1824
1848
  self.__adapt_to_network_changes()
1825
1849
 
1826
1850
  if not isinstance(model_uncertainty, ModelUncertainty):
1827
1851
  raise TypeError("'model_uncertainty' must be an instance of " +
1828
- "'epyt_flow.uncertainties.ModelUncertainty' but not of " +
1852
+ "'epyt_flow.uncertainty.ModelUncertainty' but not of " +
1829
1853
  f"'{type(model_uncertainty)}'")
1830
1854
 
1831
1855
  self.__model_uncertainty = model_uncertainty
@@ -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):
@@ -1647,6 +1647,42 @@ class SensorConfig(JsonSerializable):
1647
1647
  """
1648
1648
  return deepcopy(self.__sensors_id_to_idx)
1649
1649
 
1650
+ def get_as_dict(self) -> dict:
1651
+ """
1652
+ Gets the sensor configuration as a dictionary.
1653
+
1654
+ Returns
1655
+ -------
1656
+ `dict`
1657
+ Dictionary of set sensors -- the keys are the sensor types.
1658
+ """
1659
+ r = {}
1660
+
1661
+ if self.__pressure_sensors != []:
1662
+ r["pressure"] = self.__pressure_sensors
1663
+ if self.__flow_sensors != []:
1664
+ r["flow"] = self.__flow_sensors
1665
+ if self.__demand_sensors != []:
1666
+ r["demand"] = self.__demand_sensors
1667
+ if self.__tank_volume_sensors != []:
1668
+ r["tank_volume"] = self.__tank_volume_sensors
1669
+ if self.__valve_state_sensors != []:
1670
+ r["valve_state"] = self.__valve_state_sensors
1671
+ if self.__pump_state_sensors != []:
1672
+ r["pump_state"] = self.__pump_state_sensors
1673
+ if self.__quality_node_sensors != []:
1674
+ r["node_quality"] = self.__quality_node_sensors
1675
+ if self.__quality_link_sensors != []:
1676
+ r["link_quality"] = self.__quality_link_sensors
1677
+ if self.__bulk_species_node_sensors != {}:
1678
+ r["node_bulk_species"] = self.__bulk_species_node_sensors
1679
+ if self.__bulk_species_link_sensors != {}:
1680
+ r["link_bulk_species"] = self.__bulk_species_link_sensors
1681
+ if self.__surface_species_sensors != {}:
1682
+ r["surface_species"] = self.__surface_species_sensors
1683
+
1684
+ return r
1685
+
1650
1686
  def get_attributes(self) -> dict:
1651
1687
  attr = {"nodes": self.__nodes, "links": self.__links,
1652
1688
  "valves": self.__valves, "pumps": self.__pumps,
@@ -1719,7 +1755,7 @@ class SensorConfig(JsonSerializable):
1719
1755
  f"pump_id_to_idx: {self.__pump_id_to_idx} tank_id_to_idx: {self.__tank_id_to_idx} " +\
1720
1756
  f"valve_id_to_idx: {self.__valve_id_to_idx} " +\
1721
1757
  f"bulkspecies_id_to_idx: {self.__bulkspecies_id_to_idx} " +\
1722
- f"surfacespecies_id_to_idx: {self.__surfacespecies_id_to_idx}" +\
1758
+ f"surfacespecies_id_to_idx: {self.__surfacespecies_id_to_idx} " +\
1723
1759
  f"pressure_sensors: {self.__pressure_sensors} flow_sensors: {self.__flow_sensors} " +\
1724
1760
  f"demand_sensors: {self.__demand_sensors} " +\
1725
1761
  f"quality_node_sensors: {self.__quality_node_sensors} " +\
epyt_flow/topology.py CHANGED
@@ -7,6 +7,8 @@ from typing import Any
7
7
  import numpy as np
8
8
  import networkx as nx
9
9
  from scipy.sparse import bsr_array
10
+ from geopandas import GeoDataFrame
11
+ from shapely.geometry import Point, LineString
10
12
 
11
13
  from .serialization import serializable, JsonSerializable, NETWORK_TOPOLOGY_ID
12
14
 
@@ -55,9 +57,15 @@ class NetworkTopology(nx.Graph, JsonSerializable):
55
57
  Path to .inp file to which this topology belongs.
56
58
  nodes : `list[tuple[str, dict]]`
57
59
  List of all nodes -- i.e. node ID and node information such as type and elevation.
58
- links : `list[tuple[tuple[str, str], dict]]`
60
+ links : `list[tuple[str, tuple[str, str], dict]]`
59
61
  List of all links/pipes -- i.e. link ID, ID of connecting nodes, and link information
60
62
  such as pipe diameter, length, etc.
63
+ pumps : `dict`
64
+ List of all pumps -- i.e. valve ID, and information such as
65
+ pump type and connecting nodes.
66
+ valves : `dict`
67
+ List of all valves -- i.e. valve ID, and information such as
68
+ valve type and connecting nodes.
61
69
  units : `int`
62
70
  Measurement units category -- i.e. US Customary or SI Metric.
63
71
 
@@ -68,12 +76,16 @@ class NetworkTopology(nx.Graph, JsonSerializable):
68
76
  """
69
77
  def __init__(self, f_inp: str, nodes: list[tuple[str, dict]],
70
78
  links: list[tuple[str, tuple[str, str], dict]],
79
+ pumps: dict,
80
+ valves: dict,
71
81
  units: int = None,
72
82
  **kwds):
73
83
  super().__init__(name=f_inp, **kwds)
74
84
 
75
85
  self.__nodes = nodes
76
86
  self.__links = links
87
+ self.__pumps = pumps
88
+ self.__valves = valves
77
89
  self.__units = units
78
90
 
79
91
  if units is None:
@@ -87,15 +99,16 @@ class NetworkTopology(nx.Graph, JsonSerializable):
87
99
  self.add_node(node_id, info={"elevation": node_elevation, "type": node_type})
88
100
 
89
101
  for link_id, link, link_info in links:
102
+ link_type = link_info["type"]
90
103
  link_diameter = link_info["diameter"]
91
104
  link_length = link_info["length"]
92
105
  self.add_edge(link[0], link[1], length=link_length,
93
- info={"id": link_id, "nodes": link, "diameter": link_diameter,
94
- "length": link_length})
106
+ info={"id": link_id, "type": link_type, "nodes": link,
107
+ "diameter": link_diameter, "length": link_length})
95
108
 
96
109
  def convert_units(self, units: int) -> Any:
97
110
  """
98
- Converts this instance to a :class:`epyt_flow.topology.NetworkTopology` instance
111
+ Converts this instance to a :class:`~epyt_flow.topology.NetworkTopology` instance
99
112
  where everything is measured in given measurement units category
100
113
  (US Customary or SI Metric).
101
114
 
@@ -111,7 +124,7 @@ class NetworkTopology(nx.Graph, JsonSerializable):
111
124
 
112
125
  Returns
113
126
  -------
114
- :class:`epyt_flow.topology.NetworkTopology`
127
+ :class:`~epyt_flow.topology.NetworkTopology`
115
128
  Network topology with the new measurements units.
116
129
  """
117
130
  if self.__units is None:
@@ -161,7 +174,8 @@ class NetworkTopology(nx.Graph, JsonSerializable):
161
174
 
162
175
  links.append((link_id, link_nodes, link_info))
163
176
 
164
- return NetworkTopology(f_inp=self.name, nodes=nodes, links=links, units=units)
177
+ return NetworkTopology(f_inp=self.name, nodes=nodes, links=links, pumps=self.pumps,
178
+ valves=self.valves, units=units)
165
179
 
166
180
  def get_all_nodes(self) -> list[str]:
167
181
  """
@@ -185,6 +199,98 @@ class NetworkTopology(nx.Graph, JsonSerializable):
185
199
  """
186
200
  return [(link_id, end_points) for link_id, end_points, _ in self.__links]
187
201
 
202
+ def get_all_junctions(self) -> list[str]:
203
+ """
204
+ Gets all junctions -- i.e. nodes that are not tanks or reservoirs.
205
+
206
+ Returns
207
+ -------
208
+ `list[str]`
209
+ List of all junctions.
210
+ """
211
+ r = []
212
+
213
+ for node_id in self.get_all_nodes():
214
+ if self.get_node_info(node_id)["type"] == "JUNCTION":
215
+ r.append(node_id)
216
+
217
+ return r
218
+
219
+ def get_all_tanks(self) -> list[str]:
220
+ """
221
+ Gets all tanks -- i.e. nodes that are not junctions or reservoirs.
222
+
223
+ Returns
224
+ -------
225
+ `list[str]`
226
+ List of all tanks.
227
+ """
228
+ r = []
229
+
230
+ for node_id in self.get_all_nodes():
231
+ if self.get_node_info(node_id)["type"] == "TANK":
232
+ r.append(node_id)
233
+
234
+ return r
235
+
236
+ def get_all_reservoirs(self) -> list[str]:
237
+ """
238
+ Gets all reservoirs -- i.e. nodes that are not junctions or tanks.
239
+
240
+ Returns
241
+ -------
242
+ `list[str]`
243
+ List of all reservoirs.
244
+ """
245
+ r = []
246
+
247
+ for node_id in self.get_all_nodes():
248
+ if self.get_node_info(node_id)["type"] == "RESERVOIR":
249
+ r.append(node_id)
250
+
251
+ return r
252
+
253
+ def get_all_pipes(self) -> list[tuple[str, tuple[str, str]]]:
254
+ """
255
+ Gets all pipes -- i.e. links that not valves or pumps.
256
+
257
+ Returns
258
+ -------
259
+ `list[tuple[str, tuple[str, str]]]`
260
+ List of all pipes -- (link ID, (left node ID, right node ID)).
261
+ """
262
+ r = []
263
+
264
+ for link_id, link_nodes in self.get_all_links():
265
+ link_info = self.get_link_info(link_id)
266
+
267
+ if link_info["type"] == "PIPE":
268
+ r.append((link_id, link_nodes))
269
+
270
+ return r
271
+
272
+ def get_all_pumps(self) -> list[str]:
273
+ """
274
+ Gets the IDs of all pumps.
275
+
276
+ Returns
277
+ -------
278
+ `list[str]`
279
+ Pump IDs.
280
+ """
281
+ return self.__pumps.keys()
282
+
283
+ def get_all_valves(self) -> list[str]:
284
+ """
285
+ Gets the IDs of all valves.
286
+
287
+ Returns
288
+ -------
289
+ `list[str]`
290
+ Valve IDs.
291
+ """
292
+ return self.__valves.keys()
293
+
188
294
  def get_node_info(self, node_id: str) -> dict:
189
295
  """
190
296
  Gets all information (e.g. elevation, type, etc.) associated with a given node.
@@ -207,17 +313,19 @@ class NetworkTopology(nx.Graph, JsonSerializable):
207
313
 
208
314
  def get_link_info(self, link_id: str) -> dict:
209
315
  """
210
- Gets all information (e.g. diameter, length, etc.) associated with a given link/pipe.
316
+ Gets all information (e.g. diameter, length, etc.) associated with a given link.
317
+
318
+ Note that links can be pipes, pumps, or valves.
211
319
 
212
320
  Parameters
213
321
  ----------
214
322
  link_id : `str`
215
- ID of the link/pipe.
323
+ ID of the link.
216
324
 
217
325
  Returns
218
326
  -------
219
327
  `dict`
220
- Information associated with the given link/pipe.
328
+ Information associated with the given link.
221
329
  """
222
330
  for link_id_, link_nodes, link_info in self.__links:
223
331
  if link_id_ == link_id:
@@ -225,6 +333,68 @@ class NetworkTopology(nx.Graph, JsonSerializable):
225
333
 
226
334
  raise ValueError(f"Unknown link '{link_id}'")
227
335
 
336
+ def get_pump_info(self, pump_id: str) -> dict:
337
+ """
338
+ Gets all information associated with a given pump.
339
+
340
+ Parameters
341
+ ----------
342
+ pump_id : `str`
343
+ ID of the pump.
344
+
345
+ Returns
346
+ -------
347
+ `dict`
348
+ Pump information.
349
+ """
350
+ if pump_id in self.__pumps:
351
+ return self.__pumps[pump_id]
352
+ else:
353
+ raise ValueError(f"Unknown pump: '{pump_id}'")
354
+
355
+ def get_valve_info(self, valve_id: str) -> dict:
356
+ """
357
+ Gets all information associated with a given valve.
358
+
359
+ Parameters
360
+ ----------
361
+ valve_id : `str`
362
+ ID of the valve.
363
+
364
+ Returns
365
+ -------
366
+ `dict`
367
+ Valve information.
368
+ """
369
+ if valve_id in self.__valves:
370
+ return self.__valves[valve_id]
371
+ else:
372
+ raise ValueError(f"Unknown valve: '{valve_id}'")
373
+
374
+ @property
375
+ def pumps(self) -> dict:
376
+ """
377
+ Gets all pumps -- i.e. ID and associated information such as the pump type.
378
+
379
+ Returns
380
+ -------
381
+ `dict`
382
+ All pumps and their associated information.
383
+ """
384
+ return deepcopy(self.__pumps)
385
+
386
+ @property
387
+ def valves(self) -> dict:
388
+ """
389
+ Gets all valves -- i.e. ID and associated information such as the valve type.
390
+
391
+ Returns
392
+ -------
393
+ `dict`
394
+ All valves and their associated information.
395
+ """
396
+ return deepcopy(self.__valves)
397
+
228
398
  @property
229
399
  def units(self) -> int:
230
400
  """
@@ -251,18 +421,120 @@ class NetworkTopology(nx.Graph, JsonSerializable):
251
421
  self.get_all_nodes() == other.get_all_nodes() \
252
422
  and all(link_a[0] == link_b[0] and all(link_a[1] == link_b[1])
253
423
  for link_a, link_b in zip(self.get_all_links(), other.get_all_links())) \
254
- and self.__units == other.units
424
+ and self.__units == other.units \
425
+ and self.__pumps == other.pumps \
426
+ and self.__valves == other.valves
255
427
 
256
428
  def __str__(self) -> str:
257
429
  return f"f_inp: {self.name} nodes: {self.__nodes} links: {self.__links} " +\
430
+ f"pumps: {self.__pumps} valves: {self.__valves} " +\
258
431
  f"units: {unitscategoryid_to_str(self.__units)}"
259
432
 
260
433
  def get_attributes(self) -> dict:
261
434
  return super().get_attributes() | {"f_inp": self.name,
262
435
  "nodes": self.__nodes,
263
436
  "links": self.__links,
437
+ "pumps": self.__pumps,
438
+ "valves": self.__valves,
264
439
  "units": self.__units}
265
440
 
441
+ def to_gis(self, coord_reference_system: str = None, pumps_as_points: bool = False,
442
+ valves_as_points: bool = False) -> dict:
443
+ """
444
+ Gets the network topology as a dictionary of `geopandas.GeoDataFrames` instances --
445
+ i.e. each quantity (nodes, links/pipes, valves, etc.) is represented by a
446
+ `geopandas.GeoDataFrames` instance.
447
+
448
+ Parameters
449
+ ----------
450
+ coord_reference_system : `str`, optional
451
+ Coordinate reference system.
452
+
453
+ The default is None.
454
+ pumps_as_points : `bool`, optional
455
+ If True, pumps are represented by points, otherwise by lines.
456
+
457
+ The default is False.
458
+
459
+ valves_as_points : `bool`, optional
460
+ If True, valves are represented by points, otherwise by lines.
461
+
462
+ The default is False.
463
+
464
+ Returns
465
+ -------
466
+ `dict`
467
+ Network topology as a dictionary of `geopandas.GeoDataFrames` instances.
468
+ If a quantity does not exist, the data frame will be None.
469
+ """
470
+ gis = {"nodes": None, "links": None,
471
+ "tanks": None, "reservoirs": None,
472
+ "valves": None, "pumps": None}
473
+
474
+ # Nodes
475
+ node_data = {"id": [], "type": [], "elevation": [], "geometry": []}
476
+ tank_data = {"id": [], "elevation": [], "diameter": [], "geometry": []}
477
+ reservoir_data = {"id": [], "elevation": [], "geometry": []}
478
+ for node_id in self.get_all_nodes():
479
+ node_info = self.get_node_info(node_id)
480
+
481
+ node_data["id"].append(node_id)
482
+ node_data["type"].append(node_info["type"])
483
+ node_data["elevation"].append(node_info["elevation"])
484
+ node_data["geometry"].append(Point(node_info["coord"]))
485
+
486
+ if node_info["type"] == "TANK":
487
+ tank_data["id"].append(node_id)
488
+ tank_data["elevation"].append(node_info["elevation"])
489
+ tank_data["diameter"].append(node_info["diameter"])
490
+ tank_data["geometry"].append(Point(node_info["coord"]))
491
+ elif node_info["type"] == "RESERVOIR":
492
+ reservoir_data["id"].append(node_id)
493
+ reservoir_data["elevation"].append(node_info["elevation"])
494
+ reservoir_data["geometry"].append(Point(node_info["coord"]))
495
+
496
+ gis["nodes"] = GeoDataFrame(node_data, crs=coord_reference_system)
497
+ gis["tanks"] = GeoDataFrame(tank_data, crs=coord_reference_system)
498
+ gis["reservoirs"] = GeoDataFrame(reservoir_data, crs=coord_reference_system)
499
+
500
+ # Links
501
+ pipe_data = {"id": [], "type": [], "end_point_a": [], "end_point_b": [],
502
+ "length": [], "diameter": [], "geometry": []}
503
+ valve_data = {"id": [], "type": [], "geometry": []}
504
+ pump_data = {"id": [], "type": [], "geometry": []}
505
+ for link_id, link_nodes in self.get_all_links():
506
+ link_info = self.get_link_info(link_id)
507
+ end_points_coord = [self.get_node_info(n)["coord"] for n in link_nodes]
508
+
509
+ if link_info["type"] == "PIPE":
510
+ pipe_data["id"].append(link_id)
511
+ pipe_data["type"].append(link_info["type"])
512
+ pipe_data["end_point_a"].append(link_nodes[0])
513
+ pipe_data["end_point_b"].append(link_nodes[1])
514
+ pipe_data["length"].append(link_info["length"])
515
+ pipe_data["diameter"].append(link_info["diameter"])
516
+ pipe_data["geometry"].append(LineString(end_points_coord))
517
+ elif link_info["type"] == "PUMP":
518
+ pump_data["id"].append(link_id)
519
+ pump_data["type"].append(self.get_pump_info(link_id)["type"])
520
+ if pumps_as_points is True:
521
+ pump_data["geometry"].append(Point(end_points_coord[0]))
522
+ else:
523
+ pump_data["geometry"].append(LineString(end_points_coord))
524
+ else: # Valve
525
+ valve_data["id"].append(link_id)
526
+ valve_data["type"].append(self.get_valve_info[link_id]["type"])
527
+ if valves_as_points is True:
528
+ valve_data["geometry"].append(Point(end_points_coord[0]))
529
+ else:
530
+ valve_data["geometry"].append(LineString(end_points_coord))
531
+
532
+ gis["pipes"] = GeoDataFrame(pipe_data, crs=coord_reference_system)
533
+ gis["valves"] = GeoDataFrame(valve_data, crs=coord_reference_system)
534
+ gis["pumps"] = GeoDataFrame(pump_data, crs=coord_reference_system)
535
+
536
+ return gis
537
+
266
538
  def get_adj_matrix(self) -> bsr_array:
267
539
  """
268
540
  Gets the adjacency matrix of this graph.
@@ -2,6 +2,7 @@
2
2
  Module provides a class for implementing model uncertainty.
3
3
  """
4
4
  from copy import deepcopy
5
+ import warnings
5
6
  import epyt
6
7
  import numpy as np
7
8
 
@@ -29,7 +30,7 @@ class ModelUncertainty(JsonSerializable):
29
30
  Uncertainty of pipe diameters. None, in the case of no uncertainty.
30
31
 
31
32
  The default is None.
32
- demand_base_uncertainty : :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`, optional
33
+ base_demand_uncertainty : :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`, optional
33
34
  Uncertainty of base demands. None, in the case of no uncertainty.
34
35
 
35
36
  The default is None.
@@ -53,11 +54,17 @@ class ModelUncertainty(JsonSerializable):
53
54
  def __init__(self, pipe_length_uncertainty: Uncertainty = None,
54
55
  pipe_roughness_uncertainty: Uncertainty = None,
55
56
  pipe_diameter_uncertainty: Uncertainty = None,
56
- demand_base_uncertainty: Uncertainty = None,
57
+ base_demand_uncertainty: Uncertainty = None,
57
58
  demand_pattern_uncertainty: Uncertainty = None,
58
59
  elevation_uncertainty: Uncertainty = None,
59
60
  constants_uncertainty: Uncertainty = None,
60
- parameters_uncertainty: Uncertainty = None, **kwds):
61
+ parameters_uncertainty: Uncertainty = None,
62
+ demand_base_uncertainty: Uncertainty = None, **kwds):
63
+ if demand_base_uncertainty is not None:
64
+ warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
65
+ " -- support of such old files will be removed in the next release!",
66
+ DeprecationWarning)
67
+
61
68
  if pipe_length_uncertainty is not None:
62
69
  if not isinstance(pipe_length_uncertainty, Uncertainty):
63
70
  raise TypeError("'pipe_length_uncertainty' must be an instance of " +
@@ -73,11 +80,11 @@ class ModelUncertainty(JsonSerializable):
73
80
  raise TypeError("'pipe_diameter_uncertainty' must be an instance of " +
74
81
  "'epyt_flow.uncertainty.Uncertainty' but not of " +
75
82
  f"'{type(pipe_diameter_uncertainty)}'")
76
- if demand_base_uncertainty is not None:
77
- if not isinstance(demand_base_uncertainty, Uncertainty):
78
- raise TypeError("'demand_base_uncertainty' must be an instance of " +
83
+ if base_demand_uncertainty is not None:
84
+ if not isinstance(base_demand_uncertainty, Uncertainty):
85
+ raise TypeError("'base_demand_uncertainty' must be an instance of " +
79
86
  "'epyt_flow.uncertainty.Uncertainty' but not of " +
80
- f"'{type(demand_base_uncertainty)}'")
87
+ f"'{type(base_demand_uncertainty)}'")
81
88
  if demand_pattern_uncertainty is not None:
82
89
  if not isinstance(demand_pattern_uncertainty, Uncertainty):
83
90
  raise TypeError("'demand_pattern_uncertainty' must be an instance of " +
@@ -102,7 +109,7 @@ class ModelUncertainty(JsonSerializable):
102
109
  self.__pipe_length = pipe_length_uncertainty
103
110
  self.__pipe_roughness = pipe_roughness_uncertainty
104
111
  self.__pipe_diameter = pipe_diameter_uncertainty
105
- self.__demand_base = demand_base_uncertainty
112
+ self.__base_demand = base_demand_uncertainty
106
113
  self.__demand_pattern = demand_pattern_uncertainty
107
114
  self.__elevation = elevation_uncertainty
108
115
  self.__constants = constants_uncertainty
@@ -147,16 +154,16 @@ class ModelUncertainty(JsonSerializable):
147
154
  return deepcopy(self.__pipe_diameter)
148
155
 
149
156
  @property
150
- def demand_base(self) -> Uncertainty:
157
+ def base_demand(self) -> Uncertainty:
151
158
  """
152
- Gets the demand base uncertainty.
159
+ Gets the base demand uncertainty.
153
160
 
154
161
  Returns
155
162
  -------
156
163
  :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
157
164
  Demand base uncertainty.
158
165
  """
159
- return deepcopy(self.__demand_base)
166
+ return deepcopy(self.__base_demand)
160
167
 
161
168
  @property
162
169
  def demand_pattern(self) -> Uncertainty:
@@ -210,7 +217,7 @@ class ModelUncertainty(JsonSerializable):
210
217
  return super().get_attributes() | {"pipe_length_uncertainty": self.__pipe_length,
211
218
  "pipe_roughness_uncertainty": self.__pipe_roughness,
212
219
  "pipe_diameter_uncertainty": self.__pipe_diameter,
213
- "demand_base_uncertainty": self.__demand_base,
220
+ "base_demand_uncertainty": self.__base_demand,
214
221
  "demand_pattern_uncertainty": self.__demand_pattern,
215
222
  "elevation_uncertainty": self.__elevation,
216
223
  "constants_uncertainty": self.__constants,
@@ -224,14 +231,14 @@ class ModelUncertainty(JsonSerializable):
224
231
  return self.__pipe_length == other.pipe_length \
225
232
  and self.__pipe_roughness == other.pipe_roughness \
226
233
  and self.__pipe_diameter == other.pipe_diameter \
227
- and self.__demand_base == other.demand_base \
234
+ and self.__base_demand == other.base_demand \
228
235
  and self.__demand_pattern == other.demand_pattern \
229
236
  and self.__elevation == other.elevation \
230
237
  and self.__parameters == other.parameters and self.__constants == other.constants
231
238
 
232
239
  def __str__(self) -> str:
233
240
  return f"pipe_length: {self.__pipe_length} pipe_roughness: {self.__pipe_roughness} " + \
234
- f"pipe_diameter: {self.__pipe_diameter} demand_base: {self.__demand_base} " + \
241
+ f"pipe_diameter: {self.__pipe_diameter} demand_base: {self.__base_demand} " + \
235
242
  f"demand_pattern: {self.__demand_pattern} elevation: {self.__elevation} " + \
236
243
  f"constants: {self.__constants} parameters: {self.__parameters}"
237
244
 
@@ -259,14 +266,14 @@ class ModelUncertainty(JsonSerializable):
259
266
  coeffs = self.__pipe_roughness.apply_batch(coeffs)
260
267
  epanet_api.setLinkRoughnessCoeff(coeffs)
261
268
 
262
- if self.__demand_base is not None:
269
+ if self.__base_demand is not None:
263
270
  all_nodes_idx = epanet_api.getNodeIndex()
264
271
  for node_idx in all_nodes_idx:
265
272
  n_demand_categories = epanet_api.getNodeDemandCategoriesNumber(node_idx)
266
- for demand_category in range(1, n_demand_categories):
267
- base_demand = epanet_api.getNodeBaseDemands(node_idx)[demand_category]
268
- base_demand = self.__demand_base.apply(base_demand)
269
- epanet_api.setNodeBaseDemands(node_idx, demand_category, base_demand)
273
+ for demand_category in range(n_demand_categories):
274
+ base_demand = epanet_api.getNodeBaseDemands(node_idx)[demand_category + 1]
275
+ base_demand = self.__base_demand.apply(base_demand)
276
+ epanet_api.setNodeBaseDemands(node_idx, demand_category + 1, base_demand)
270
277
 
271
278
  if self.__demand_pattern is not None:
272
279
  demand_patterns_idx = epanet_api.getNodeDemandPatternIndex()
epyt_flow/utils.py CHANGED
@@ -153,8 +153,10 @@ def plot_timeseries_data(data: np.ndarray, labels: list[str] = None, x_axis_labe
153
153
 
154
154
 
155
155
  def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
156
- confidence_interval: np.ndarray = None, show: bool = True,
157
- ax: matplotlib.axes.Axes = None) -> matplotlib.axes.Axes:
156
+ confidence_interval: np.ndarray = None,
157
+ x_axis_label: str = None, y_axis_label: str = None,
158
+ show: bool = True, ax: matplotlib.axes.Axes = None
159
+ ) -> matplotlib.axes.Axes:
158
160
  """
159
161
  Plots the prediction (e.g. forecast) of *single* time series together with the
160
162
  ground truth time series. In addition, confidence intervals can be plotted as well.
@@ -169,6 +171,14 @@ def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
169
171
  Confidence interval (upper and lower value) for each prediction in `y_pred`.
170
172
  If not None, the confidence interval is plotted as well.
171
173
 
174
+ The default is None.
175
+ x_axis_label : `str`, optional
176
+ X axis label.
177
+
178
+ The default is None.
179
+ y_axis_label : `str`, optional
180
+ Y axis label.
181
+
172
182
  The default is None.
173
183
  show : `bool`, optional
174
184
  If True, the plot/figure is shown in a window.
@@ -198,6 +208,14 @@ def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
198
208
  raise ValueError("'y_pred' must be a 1d array")
199
209
  if len(y.shape) != 1:
200
210
  raise ValueError("'y' must be a 1d array")
211
+ if x_axis_label is not None:
212
+ if not isinstance(x_axis_label, str):
213
+ raise TypeError("'x_axis_label' must be an instance of 'str' " +
214
+ f"but not of '{type(x_axis_label)}'")
215
+ if y_axis_label is not None:
216
+ if not isinstance(y_axis_label, str):
217
+ raise TypeError("'y_axis_label' must be an instance of 'str' " +
218
+ f"but not of '{type(y_axis_label)}'")
201
219
  if not isinstance(show, bool):
202
220
  raise TypeError(f"'show' must be an instance of 'bool' but not of '{type(show)}'")
203
221
  if ax is not None:
@@ -214,10 +232,15 @@ def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
214
232
  y_pred - confidence_interval[0],
215
233
  y_pred + confidence_interval[1],
216
234
  alpha=0.5)
217
- ax.plot(y_pred, ".-", label="Prediction")
218
235
  ax.plot(y, ".-", label="Ground truth")
236
+ ax.plot(y_pred, ".-", label="Prediction")
219
237
  ax.legend()
220
238
 
239
+ if x_axis_label is not None:
240
+ ax.set_xlabel(x_axis_label)
241
+ if y_axis_label is not None:
242
+ ax.set_ylabel(y_axis_label)
243
+
221
244
  if show is True and fig is not None:
222
245
  plt.show()
223
246
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: epyt-flow
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: EPyT-Flow -- EPANET Python Toolkit - Flow
5
5
  Author-email: André Artelt <aartelt@techfak.uni-bielefeld.de>, "Marios S. Kyriakou" <kiriakou.marios@ucy.ac.cy>, "Stelios G. Vrachimis" <vrachimis.stelios@ucy.ac.cy>
6
6
  License: MIT License
@@ -30,6 +30,7 @@ Requires-Dist: tqdm >=4.66.2
30
30
  Requires-Dist: openpyxl >=3.1.2
31
31
  Requires-Dist: falcon >=3.1.3
32
32
  Requires-Dist: multiprocess >=0.70.16
33
+ Requires-Dist: geopandas >=0.14.4
33
34
  Requires-Dist: psutil
34
35
 
35
36
  [![pypi](https://img.shields.io/pypi/v/epyt-flow.svg)](https://pypi.org/project/epyt-flow/)
@@ -1,9 +1,9 @@
1
- epyt_flow/VERSION,sha256=2RXMldbKj0euKXcT7UbU5cXZnd0p_Dxh4mO98wXytbA,6
1
+ epyt_flow/VERSION,sha256=oK1QZAE5pST4ZZEVcUW_HUZ06pwGW_6iFVjw97BEMMg,6
2
2
  epyt_flow/__init__.py,sha256=KNDiPWiHdB9a5ZF1ipjA1uoq61TwU2ThjaStpvSLBtY,1742
3
3
  epyt_flow/metrics.py,sha256=kvt42pzZrUR9PSlCyK4uq5kj6UlYHkt7OcCjLnI1RQE,12883
4
4
  epyt_flow/serialization.py,sha256=nBcwc3aMUbHF0zW8Nvpc49kKeLPh75blc3gzjKDR1ok,12893
5
- epyt_flow/topology.py,sha256=LTWqKwXVRfg3zYN16x5_ZH-wUj8U5hB2spLWzlEnsqM,14221
6
- epyt_flow/utils.py,sha256=-1DFDpST_J86AB6pd2ufe_QQ06_tvQqB-5wD8DXacaU,11507
5
+ epyt_flow/topology.py,sha256=yKIspoX4uoo9rAeVR5eWJ6D91QNozHzvzjiry9bFkNI,23424
6
+ epyt_flow/utils.py,sha256=AB2MuknQ_16UE-URQe1WShIS7dmSyFwZYVHxMVT539k,12379
7
7
  epyt_flow/EPANET/compile_linux.sh,sha256=wcrDyiB8NkivmaC-X9FI2WxhY3IJqDLiyIbVTv2XEPY,489
8
8
  epyt_flow/EPANET/compile_macos.sh,sha256=1K33-bPdgr01EIf87YUvmOFHXyOkBWI6mKXQ8x1Hzmo,504
9
9
  epyt_flow/EPANET/EPANET/SRC_engines/AUTHORS,sha256=yie5yAsEEPY0984PmkSRUdqEU9rVvRSGGWmjxdwCYMU,925
@@ -87,7 +87,7 @@ epyt_flow/data/benchmarks/batadal_data.py,sha256=oIzcysGivMPAgrfzrk5l8i-j6Ii96DP
87
87
  epyt_flow/data/benchmarks/battledim.py,sha256=K0j9tbfxuXJmqBQY_e5XBJZGDYavaOZ7efkCT96Fb8A,20167
88
88
  epyt_flow/data/benchmarks/battledim_data.py,sha256=0vHm-2eAiLv6U-n5dqUUWS1o_szFRy9mVJ3eqDRp4PE,3373
89
89
  epyt_flow/data/benchmarks/gecco_water_quality.py,sha256=1buZRJiNf4jsqWYg4Ud90GhqaiLVo4yij3RAZJkzsqE,10985
90
- epyt_flow/data/benchmarks/leakdb.py,sha256=jMMSxJ6XrjOo5b91sGwplT5mTcv3k9Xr-TnWz3BhMbw,24952
90
+ epyt_flow/data/benchmarks/leakdb.py,sha256=Z_ah6FXAyN4M7dn4jH0qYFmKTtViNHd6Jn2L9I6nMYg,24952
91
91
  epyt_flow/data/benchmarks/leakdb_data.py,sha256=FNssgMkC1wqWVlaOrrihr4Od9trEZY7KeK5KuBeRMvM,507058
92
92
  epyt_flow/data/benchmarks/water_usage.py,sha256=FLqjff3pha33oEU9ZM3UGPXn9eJJumsJH8Gdj7YFX3A,4778
93
93
  epyt_flow/gym/__init__.py,sha256=KNTDtPTEtHwZ4ehHfj9qGw81Z9acFqPIgMzYUzH5_uM,115
@@ -110,30 +110,30 @@ epyt_flow/rest_api/scenario/handlers.py,sha256=bDqMa-jM4hOMVqs0XvQJtHbQDZx9FP40G
110
110
  epyt_flow/rest_api/scenario/simulation_handlers.py,sha256=oY1Ch6ZQgYT_5WeE1I7tvGqpDKlT664GHLdmcVKP7ek,5905
111
111
  epyt_flow/rest_api/scenario/uncertainty_handlers.py,sha256=uuu6AP11ZZUp2P3Dnukjg5ZTjyYKljlubg1xLN1GnXY,4048
112
112
  epyt_flow/simulation/__init__.py,sha256=VGGJqJRUoXZjKJ0-m6KPp3JQqD_1TFW0pofLgkwZJ8M,164
113
- epyt_flow/simulation/parallel_simulation.py,sha256=mMevycgtnMjV2FDq50WS4HjAdcOlI72Aj6FBX4HZDtc,6508
114
- epyt_flow/simulation/scenario_config.py,sha256=6xZD3Gwje-isiSaoKCXLkX0R81SVDH8LojsXdDlH8to,26540
115
- epyt_flow/simulation/scenario_simulator.py,sha256=Yf2oo3fMfvxrrW3nX8d6y2plrNcGZ-DBK-5mpLtplms,92070
116
- epyt_flow/simulation/scenario_visualizer.py,sha256=2gr1o731VLlhA8wQKgrLT94M43FsBNKjG_eucZPHy9A,2186
117
- epyt_flow/simulation/sensor_config.py,sha256=9pUzvRm4HSF8zZ-Hu4pdpxzTjpNSdNmDvcPhRaRFNAI,80175
113
+ epyt_flow/simulation/parallel_simulation.py,sha256=VmC7xemjxRB_N0fx1AAQ7ux82tnyTi7jk7jfFpeg7gM,6523
114
+ epyt_flow/simulation/scenario_config.py,sha256=_Zs0EPyfet0mTLpAt-oMN3k7jCW3x7_VbOQHpHVntrA,26597
115
+ epyt_flow/simulation/scenario_simulator.py,sha256=1amMxO-PVHBU_AN6K9Ga6xzFmpaX2ZZnhwkirtj6IgY,93036
116
+ epyt_flow/simulation/scenario_visualizer.py,sha256=fpj67zl69q-byg7Oxocqhmu1S3P7B3ROCkSYzWyM--0,2187
117
+ epyt_flow/simulation/sensor_config.py,sha256=MGeerEnLhyFLE6XA2p-X9Swd4k0o0T7QboeGuTUFq7I,81584
118
118
  epyt_flow/simulation/events/__init__.py,sha256=tIdqzs7_Cus4X2kbZG4Jl2zs-zsk_4rnajFOCvL0zlI,185
119
119
  epyt_flow/simulation/events/actuator_events.py,sha256=2_MPYbYO9As6fMkm5Oy9pjSB9kCvFuKpGu8ykYDAydg,7903
120
120
  epyt_flow/simulation/events/event.py,sha256=kARPV20XCAl6zxnJwI9U7ICtZUPACO_rgAmtHm1mGCs,2603
121
- epyt_flow/simulation/events/leakages.py,sha256=lDZNKOtVtQsKYhAuJ5Mt7tG6IfHX1436eSkjYTkQiZk,15267
121
+ epyt_flow/simulation/events/leakages.py,sha256=g4asHeB3s7mJekY7WTdmTFb6NsoHov8lEciclS3um0I,15275
122
122
  epyt_flow/simulation/events/sensor_faults.py,sha256=XX6k-GJh9RWZ4x54eGj9um-Ir9Eq41tY_9pRSCeYeqc,8447
123
123
  epyt_flow/simulation/events/sensor_reading_attack.py,sha256=bo5VavArN0wD5AHbIXJC9NFGZ7KR1uyWE6tBtwj0k9I,7538
124
124
  epyt_flow/simulation/events/sensor_reading_event.py,sha256=rQ-CmdpSUyZzDFYwNUGH2jGoj0oyU-aAb-7E8Oshhqw,6785
125
125
  epyt_flow/simulation/events/system_event.py,sha256=0KI2iaAaOyC9Y-FIfFVazeKT_4ORQRp26gWyMBUu_3c,2396
126
126
  epyt_flow/simulation/scada/__init__.py,sha256=ZFAxJVqwEVsgiyFilFetnb13gPhZg1JEOPWYvKIJT4c,90
127
127
  epyt_flow/simulation/scada/advanced_control.py,sha256=Enox02ggt36HdFLX7ZNxgxuqsTEeu9AACHrzU8CXGrg,4489
128
- epyt_flow/simulation/scada/scada_data.py,sha256=NfGDGrQ7HIXTcShUaGeCALTvGmM5LAJW0Bgmk0H4UQg,104867
129
- epyt_flow/simulation/scada/scada_data_export.py,sha256=YlA-6R6XpQ28POyKsw1eEe23gMAuDFvwBuDUN-ciu2Y,11213
128
+ epyt_flow/simulation/scada/scada_data.py,sha256=WaFSppBHvJOwSyLYNs9Pu2GngSzWsfkpqPfBQoeiEcU,105101
129
+ epyt_flow/simulation/scada/scada_data_export.py,sha256=0BwDgV-5qZx17wIyWQ8Nl2TPgho3mBI49027RDq8sDA,11217
130
130
  epyt_flow/uncertainty/__init__.py,sha256=ZRjuJL9rDpWVSdPwObPxFpEmMTcgAl3VmPOsS6cIyGg,89
131
- epyt_flow/uncertainty/model_uncertainty.py,sha256=DAgyfvdTQvOVMyduuPIpPB_8e3GIMNpehu6kh0jFg0s,13592
131
+ epyt_flow/uncertainty/model_uncertainty.py,sha256=-2QT2AffZerKZyZ_w_mmeqYpfBALyPDvV61sCrvcK1o,13966
132
132
  epyt_flow/uncertainty/sensor_noise.py,sha256=zJVULxnxVPSSqc6UW0iwZ9O-HGf9dn4CwScPqf4yCY0,2324
133
133
  epyt_flow/uncertainty/uncertainties.py,sha256=X-o7GZUC0HELtzpoXIAJaAeYOw35N05TuRoSmStcCpI,17669
134
134
  epyt_flow/uncertainty/utils.py,sha256=gq66c9-QMOxOqI6wgWLyFxjVV0fbG0_8Yzd6mQjNYNo,5315
135
- epyt_flow-0.3.0.dist-info/LICENSE,sha256=-4hYIY2BLmCkdOv2_PehEwlnMKTCes8_oyIUXjKtkug,1076
136
- epyt_flow-0.3.0.dist-info/METADATA,sha256=tnq12BhftSSxemSI93ONtLRs4K4eT8R_I71NENxUuX4,7053
137
- epyt_flow-0.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
138
- epyt_flow-0.3.0.dist-info/top_level.txt,sha256=Wh_kd7TRL8ownCw3Y3dxx-9C0iTSk6wNauv_NX9JcrY,10
139
- epyt_flow-0.3.0.dist-info/RECORD,,
135
+ epyt_flow-0.5.0.dist-info/LICENSE,sha256=-4hYIY2BLmCkdOv2_PehEwlnMKTCes8_oyIUXjKtkug,1076
136
+ epyt_flow-0.5.0.dist-info/METADATA,sha256=a7kEbUctgxuMBJ06GNrwJnAlr_arMtcxHg-TZzSiD8M,7087
137
+ epyt_flow-0.5.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
138
+ epyt_flow-0.5.0.dist-info/top_level.txt,sha256=Wh_kd7TRL8ownCw3Y3dxx-9C0iTSk6wNauv_NX9JcrY,10
139
+ epyt_flow-0.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5