epyt-flow 0.8.1__py3-none-any.whl → 0.9.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/__init__.py +1 -0
- epyt_flow/data/benchmarks/batadal.py +1 -1
- epyt_flow/data/benchmarks/battledim.py +4 -3
- epyt_flow/data/benchmarks/gecco_water_quality.py +4 -4
- epyt_flow/data/benchmarks/leakdb.py +7 -7
- epyt_flow/data/benchmarks/water_usage.py +2 -2
- epyt_flow/data/networks.py +1 -1
- epyt_flow/gym/control_gyms.py +2 -2
- epyt_flow/gym/scenario_control_env.py +1 -1
- epyt_flow/metrics.py +28 -28
- epyt_flow/models/sensor_interpolation_detector.py +3 -3
- epyt_flow/rest_api/base_handler.py +4 -4
- epyt_flow/rest_api/scada_data/data_handlers.py +11 -11
- epyt_flow/rest_api/scada_data/export_handlers.py +2 -2
- epyt_flow/rest_api/scada_data/handlers.py +9 -9
- epyt_flow/rest_api/scenario/event_handlers.py +6 -6
- epyt_flow/rest_api/scenario/handlers.py +15 -15
- epyt_flow/rest_api/scenario/simulation_handlers.py +7 -7
- epyt_flow/rest_api/scenario/uncertainty_handlers.py +6 -6
- epyt_flow/serialization.py +4 -2
- epyt_flow/simulation/events/actuator_events.py +1 -1
- epyt_flow/simulation/events/leakages.py +1 -1
- epyt_flow/simulation/events/quality_events.py +16 -5
- epyt_flow/simulation/events/sensor_reading_attack.py +1 -1
- epyt_flow/simulation/events/sensor_reading_event.py +3 -3
- epyt_flow/simulation/events/system_event.py +1 -1
- epyt_flow/simulation/parallel_simulation.py +1 -1
- epyt_flow/simulation/scada/advanced_control.py +2 -2
- epyt_flow/simulation/scada/scada_data.py +117 -131
- epyt_flow/simulation/scada/scada_data_export.py +1 -1
- epyt_flow/simulation/scenario_config.py +1 -1
- epyt_flow/simulation/scenario_simulator.py +120 -26
- epyt_flow/simulation/scenario_visualizer.py +9 -9
- epyt_flow/simulation/sensor_config.py +22 -28
- epyt_flow/topology.py +2 -2
- epyt_flow/uncertainty/model_uncertainty.py +624 -147
- epyt_flow/uncertainty/sensor_noise.py +94 -19
- epyt_flow/uncertainty/uncertainties.py +4 -4
- epyt_flow/uncertainty/utils.py +7 -7
- epyt_flow/utils.py +9 -8
- {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/METADATA +1 -1
- {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/RECORD +46 -46
- {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/LICENSE +0 -0
- {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/WHEEL +0 -0
- {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -102,7 +102,7 @@ class ScadaDataExport():
|
|
|
102
102
|
|
|
103
103
|
Returns
|
|
104
104
|
-------
|
|
105
|
-
`numpy.ndarray
|
|
105
|
+
`numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
106
106
|
3-dimensional array describing all columns of the sensor readings:
|
|
107
107
|
The first dimension describes the sensor type, the second dimension
|
|
108
108
|
describes the sensor location, and the third one describes the measurement units.
|
|
@@ -5,8 +5,8 @@ from typing import Any
|
|
|
5
5
|
from copy import deepcopy
|
|
6
6
|
import os
|
|
7
7
|
import json
|
|
8
|
-
import numpy as np
|
|
9
8
|
from pathlib import Path
|
|
9
|
+
import numpy as np
|
|
10
10
|
|
|
11
11
|
from ..uncertainty import AbsoluteGaussianUncertainty, RelativeGaussianUncertainty, \
|
|
12
12
|
AbsoluteUniformUncertainty, RelativeUniformUncertainty, ModelUncertainty, \
|
|
@@ -477,7 +477,8 @@ class ScenarioSimulator():
|
|
|
477
477
|
self.close()
|
|
478
478
|
|
|
479
479
|
def save_to_epanet_file(self, inp_file_path: str, msx_file_path: str = None,
|
|
480
|
-
export_sensor_config: bool = True
|
|
480
|
+
export_sensor_config: bool = True, undo_system_events: bool = True
|
|
481
|
+
) -> None:
|
|
481
482
|
"""
|
|
482
483
|
Exports this scenario to EPANET files -- i.e. an .inp file
|
|
483
484
|
and (optionally) a .msx file if EPANET-MSX was loaded.
|
|
@@ -540,6 +541,10 @@ class ScenarioSimulator():
|
|
|
540
541
|
if write_end_section is True:
|
|
541
542
|
f_in.write("\n[END]")
|
|
542
543
|
|
|
544
|
+
if undo_system_events is True:
|
|
545
|
+
for event in self._system_events:
|
|
546
|
+
event.cleanup()
|
|
547
|
+
|
|
543
548
|
if inp_file_path is not None:
|
|
544
549
|
self.epanet_api.saveInputFile(inp_file_path)
|
|
545
550
|
self.__f_inp_in = inp_file_path
|
|
@@ -641,6 +646,10 @@ class ScenarioSimulator():
|
|
|
641
646
|
|
|
642
647
|
__override_report_section(msx_file_path, report_desc)
|
|
643
648
|
|
|
649
|
+
if undo_system_events is True:
|
|
650
|
+
for event in self._system_events:
|
|
651
|
+
event.init(self.epanet_api)
|
|
652
|
+
|
|
644
653
|
def get_flow_units(self) -> int:
|
|
645
654
|
"""
|
|
646
655
|
Gets the flow units.
|
|
@@ -938,8 +947,72 @@ class ScenarioSimulator():
|
|
|
938
947
|
for t in range(pattern_length): # Set shuffled/randomized pattern
|
|
939
948
|
self.epanet_api.setPatternValue(pattern_id, t + 1, pattern[t])
|
|
940
949
|
|
|
950
|
+
def get_pattern(self, pattern_id: str) -> np.ndarray:
|
|
951
|
+
"""
|
|
952
|
+
Returns the EPANET pattern (i.e. all multiplier factors over time) given its ID.
|
|
953
|
+
|
|
954
|
+
Parameters
|
|
955
|
+
----------
|
|
956
|
+
pattern_id : `str`
|
|
957
|
+
ID of the pattern.
|
|
958
|
+
|
|
959
|
+
Returns
|
|
960
|
+
-------
|
|
961
|
+
`numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
962
|
+
The pattern -- i.e. multiplier factors over time.
|
|
963
|
+
"""
|
|
964
|
+
pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
|
|
965
|
+
pattern_length = self.epanet_api.getPatternLengths(pattern_idx)
|
|
966
|
+
return np.array([self.epanet_api.getPatternValue(pattern_idx, t+1)
|
|
967
|
+
for t in range(pattern_length)])
|
|
968
|
+
|
|
969
|
+
def add_pattern(self, pattern_id: str, pattern: np.ndarray) -> None:
|
|
970
|
+
"""
|
|
971
|
+
Adds a pattern to the EPANET scenario.
|
|
972
|
+
|
|
973
|
+
Parameters
|
|
974
|
+
----------
|
|
975
|
+
pattern_id : `str`
|
|
976
|
+
ID of the pattern.
|
|
977
|
+
pattern : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
978
|
+
Pattern of multipliers over time.
|
|
979
|
+
"""
|
|
980
|
+
self._adapt_to_network_changes()
|
|
981
|
+
|
|
982
|
+
if not isinstance(pattern_id, str):
|
|
983
|
+
raise TypeError("'pattern_id' must be an instance of 'str' " +
|
|
984
|
+
f"but not of '{type(pattern_id)}'")
|
|
985
|
+
if not isinstance(pattern, np.ndarray):
|
|
986
|
+
raise TypeError("'pattern' must be an instance of 'numpy.ndarray' " +
|
|
987
|
+
f"but not of '{type(pattern)}'")
|
|
988
|
+
if len(pattern.shape) > 1:
|
|
989
|
+
raise ValueError(f"Inconsistent pattern shape '{pattern.shape}' " +
|
|
990
|
+
"detected. Expected a one dimensional array!")
|
|
991
|
+
|
|
992
|
+
self.epanet_api.addPattern(pattern_id, pattern)
|
|
993
|
+
|
|
994
|
+
def get_node_demand_pattern(self, node_id: str) -> np.ndarray:
|
|
995
|
+
"""
|
|
996
|
+
Returns the values of the demand pattern of a given node --
|
|
997
|
+
i.e. multiplier factors that are applied to the base demand.
|
|
998
|
+
|
|
999
|
+
Parameters
|
|
1000
|
+
----------
|
|
1001
|
+
node_id : `str`
|
|
1002
|
+
ID of the node.
|
|
1003
|
+
|
|
1004
|
+
Returns
|
|
1005
|
+
-------
|
|
1006
|
+
`numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1007
|
+
The demand pattern -- i.e. multiplier factors over time.
|
|
1008
|
+
"""
|
|
1009
|
+
node_idx = self.epanet_api.getNodeIndex(node_id)
|
|
1010
|
+
demand_category = self.epanet_api.getNodeDemandCategoriesNumber()[node_idx]
|
|
1011
|
+
demand_pattern_id = self.epanet_api.getNodeDemandPatternNameID()[demand_category][node_idx - 1]
|
|
1012
|
+
return self.get_pattern(demand_pattern_id)
|
|
1013
|
+
|
|
941
1014
|
def set_node_demand_pattern(self, node_id: str, base_demand: float, demand_pattern_id: str,
|
|
942
|
-
demand_pattern: np.ndarray) -> None:
|
|
1015
|
+
demand_pattern: np.ndarray = None) -> None:
|
|
943
1016
|
"""
|
|
944
1017
|
Sets the demand pattern (incl. base demand) at a given node.
|
|
945
1018
|
|
|
@@ -950,9 +1023,12 @@ class ScenarioSimulator():
|
|
|
950
1023
|
base_demand : `float`
|
|
951
1024
|
Base demand.
|
|
952
1025
|
demand_pattern_id : `str`
|
|
953
|
-
ID of the new demand pattern.
|
|
954
|
-
demand_pattern : `numpy.ndarray
|
|
1026
|
+
ID of the (new) demand pattern. Existing demand pattern will be overriden if it already exisits.
|
|
1027
|
+
demand_pattern : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
|
|
955
1028
|
Demand pattern over time. Final demand over time = base_demand * demand_pattern
|
|
1029
|
+
If None, the pattern demand_pattern_id is assumed to already exist.
|
|
1030
|
+
|
|
1031
|
+
The default is None.
|
|
956
1032
|
"""
|
|
957
1033
|
self._adapt_to_network_changes()
|
|
958
1034
|
|
|
@@ -964,15 +1040,26 @@ class ScenarioSimulator():
|
|
|
964
1040
|
if not isinstance(demand_pattern_id, str):
|
|
965
1041
|
raise TypeError("'demand_pattern_id' must be an instance of 'str' " +
|
|
966
1042
|
f"but not of '{type(demand_pattern_id)}'")
|
|
967
|
-
if not
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1043
|
+
if demand_pattern is not None:
|
|
1044
|
+
if not isinstance(demand_pattern, np.ndarray):
|
|
1045
|
+
raise TypeError("'demand_pattern' must be an instance of 'numpy.ndarray' " +
|
|
1046
|
+
f"but not of '{type(demand_pattern)}'")
|
|
1047
|
+
if len(demand_pattern.shape) > 1:
|
|
1048
|
+
raise ValueError(f"Inconsistent demand pattern shape '{demand_pattern.shape}' " +
|
|
1049
|
+
"detected. Expected a one dimensional array!")
|
|
973
1050
|
|
|
974
1051
|
node_idx = self.epanet_api.getNodeIndex(node_id)
|
|
975
|
-
|
|
1052
|
+
|
|
1053
|
+
if demand_pattern_id not in self.epanet_api.getPatternNameID():
|
|
1054
|
+
if demand_pattern is None:
|
|
1055
|
+
raise ValueError("'demand_pattern' can not be None if " +
|
|
1056
|
+
"'demand_pattern_id' does not already exist.")
|
|
1057
|
+
self.epanet_api.addPattern(demand_pattern_id, demand_pattern)
|
|
1058
|
+
else:
|
|
1059
|
+
if demand_pattern is not None:
|
|
1060
|
+
pattern_idx = self.epanet_api.getPatternIndex(demand_pattern_id)
|
|
1061
|
+
self.epanet_api.setPattern(pattern_idx, demand_pattern)
|
|
1062
|
+
|
|
976
1063
|
self.epanet_api.setNodeJunctionData(node_idx, self.epanet_api.getNodeElevations(node_idx),
|
|
977
1064
|
base_demand, demand_pattern_id)
|
|
978
1065
|
|
|
@@ -1654,7 +1741,7 @@ class ScenarioSimulator():
|
|
|
1654
1741
|
print("Running EPANET-MSX ...")
|
|
1655
1742
|
n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
|
|
1656
1743
|
hyd_time_step)
|
|
1657
|
-
progress_bar = iter(tqdm(range(n_iterations + 1), desc="Time steps"))
|
|
1744
|
+
progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
|
|
1658
1745
|
|
|
1659
1746
|
def __get_concentrations(init_qual=False):
|
|
1660
1747
|
if init_qual is True:
|
|
@@ -1896,7 +1983,7 @@ class ScenarioSimulator():
|
|
|
1896
1983
|
print("Running basic quality analysis using EPANET ...")
|
|
1897
1984
|
n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
|
|
1898
1985
|
requested_time_step)
|
|
1899
|
-
progress_bar = iter(tqdm(range(n_iterations + 1), desc="Time steps"))
|
|
1986
|
+
progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
|
|
1900
1987
|
|
|
1901
1988
|
# Run simulation step by step
|
|
1902
1989
|
total_time = 0
|
|
@@ -2081,7 +2168,7 @@ class ScenarioSimulator():
|
|
|
2081
2168
|
print("Running EPANET ...")
|
|
2082
2169
|
n_iterations = math.ceil(self.epanet_api.getTimeSimulationDuration() /
|
|
2083
2170
|
requested_time_step)
|
|
2084
|
-
progress_bar = iter(tqdm(range(n_iterations + 1), desc="Time steps"))
|
|
2171
|
+
progress_bar = iter(tqdm(range(n_iterations + 1), ascii=True, desc="Time steps"))
|
|
2085
2172
|
|
|
2086
2173
|
try:
|
|
2087
2174
|
# Run simulation step by step
|
|
@@ -2538,7 +2625,7 @@ class ScenarioSimulator():
|
|
|
2538
2625
|
self.set_general_parameters(quality_model={"type": "CHEM", "chemical_name": chemical_name,
|
|
2539
2626
|
"units": chemical_units})
|
|
2540
2627
|
|
|
2541
|
-
def add_quality_source(self, node_id: str, pattern: np.ndarray
|
|
2628
|
+
def add_quality_source(self, node_id: str, source_type: int, pattern: np.ndarray = None,
|
|
2542
2629
|
pattern_id: str = None, source_strength: int = 1.) -> None:
|
|
2543
2630
|
"""
|
|
2544
2631
|
Adds a new external water quality source at a particular node.
|
|
@@ -2547,8 +2634,6 @@ class ScenarioSimulator():
|
|
|
2547
2634
|
----------
|
|
2548
2635
|
node_id : `str`
|
|
2549
2636
|
ID of the node at which this external water quality source is placed.
|
|
2550
|
-
pattern : `numpy.ndarray`
|
|
2551
|
-
1d source pattern.
|
|
2552
2637
|
source_type : `int`,
|
|
2553
2638
|
Types of the external water quality source -- must be of the following
|
|
2554
2639
|
EPANET toolkit constants:
|
|
@@ -2564,6 +2649,12 @@ class ScenarioSimulator():
|
|
|
2564
2649
|
- EN_MASS Injects a given mass/minute into a node
|
|
2565
2650
|
- EN_SETPOINT Sets the concentration leaving a node to a given value
|
|
2566
2651
|
- EN_FLOWPACED Adds a given value to the concentration leaving a node
|
|
2652
|
+
pattern : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
|
|
2653
|
+
1d source pattern multipiers over time -- i.e. quality-source = source_strength * pattern.
|
|
2654
|
+
|
|
2655
|
+
If None, the pattern pattern_id is assume to already exist.
|
|
2656
|
+
|
|
2657
|
+
The default is None.
|
|
2567
2658
|
pattern_id : `str`, optional
|
|
2568
2659
|
ID of the source pattern.
|
|
2569
2660
|
|
|
@@ -2586,20 +2677,23 @@ class ScenarioSimulator():
|
|
|
2586
2677
|
"call 'enable_chemical_analysis()' before calling this function.")
|
|
2587
2678
|
if node_id not in self._sensor_config.nodes:
|
|
2588
2679
|
raise ValueError(f"Unknown node '{node_id}'")
|
|
2589
|
-
if not isinstance(pattern, np.ndarray):
|
|
2590
|
-
raise TypeError("'pattern' must be an instance of 'numpy.ndarray' " +
|
|
2591
|
-
f"but not of '{type(pattern)}'")
|
|
2592
2680
|
if not isinstance(source_type, int) or not 0 <= source_type <= 3:
|
|
2593
2681
|
raise ValueError("Invalid type of water quality source")
|
|
2594
|
-
|
|
2682
|
+
if pattern is not None:
|
|
2683
|
+
if not isinstance(pattern, np.ndarray):
|
|
2684
|
+
raise TypeError("'pattern' must be an instance of 'numpy.ndarray' " +
|
|
2685
|
+
f"but not of '{type(pattern)}'")
|
|
2686
|
+
if pattern is None and pattern_id is None:
|
|
2687
|
+
raise ValueError("'pattern_id' and 'pattern' can not be None at the same time")
|
|
2595
2688
|
if pattern_id is None:
|
|
2596
2689
|
pattern_id = f"quality_source_pattern_node={node_id}"
|
|
2597
|
-
if pattern_id in self.epanet_api.getPatternNameID():
|
|
2598
|
-
raise ValueError("Invalid 'pattern_id' -- " +
|
|
2599
|
-
f"there already exists a pattern with ID '{pattern_id}'")
|
|
2600
2690
|
|
|
2601
2691
|
node_idx = self.epanet_api.getNodeIndex(node_id)
|
|
2602
|
-
|
|
2692
|
+
|
|
2693
|
+
if pattern is None:
|
|
2694
|
+
pattern_idx = self.epanet_api.getPatternIndex(pattern_id)
|
|
2695
|
+
else:
|
|
2696
|
+
pattern_idx = self.epanet_api.addPattern(pattern_id, pattern)
|
|
2603
2697
|
|
|
2604
2698
|
self.epanet_api.api.ENsetnodevalue(node_idx, ToolkitConstants.EN_SOURCETYPE, source_type)
|
|
2605
2699
|
self.epanet_api.setNodeSourceQuality(node_idx, source_strength)
|
|
@@ -2640,7 +2734,7 @@ class ScenarioSimulator():
|
|
|
2640
2734
|
node_id : `str`
|
|
2641
2735
|
ID of the node at which this external (bulk or surface) species injection source
|
|
2642
2736
|
is placed.
|
|
2643
|
-
pattern : `numpy.ndarray
|
|
2737
|
+
pattern : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2644
2738
|
1d source pattern.
|
|
2645
2739
|
source_type : `int`,
|
|
2646
2740
|
Type of the external (bulk or surface) species injection source -- must be one of
|
|
@@ -147,7 +147,7 @@ class ScenarioVisualizer:
|
|
|
147
147
|
|
|
148
148
|
Parameters
|
|
149
149
|
----------
|
|
150
|
-
scenario : :class
|
|
150
|
+
scenario : :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
|
|
151
151
|
An instance of the `ScenarioSimulator` class, used to simulate and
|
|
152
152
|
retrieve the system topology.
|
|
153
153
|
|
|
@@ -155,7 +155,7 @@ class ScenarioVisualizer:
|
|
|
155
155
|
------
|
|
156
156
|
TypeError
|
|
157
157
|
If `scenario` is not an instance of
|
|
158
|
-
:class
|
|
158
|
+
:class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
|
|
159
159
|
|
|
160
160
|
"""
|
|
161
161
|
if not isinstance(scenario, ScenarioSimulator):
|
|
@@ -303,7 +303,7 @@ class ScenarioVisualizer:
|
|
|
303
303
|
|
|
304
304
|
Parameters
|
|
305
305
|
----------
|
|
306
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
306
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
307
307
|
The SCADA data object to retrieve link data from. If `None`, a
|
|
308
308
|
simulation is run to generate the SCADA data. Default is `None`.
|
|
309
309
|
parameter : `str`, optional
|
|
@@ -644,7 +644,7 @@ class ScenarioVisualizer:
|
|
|
644
644
|
|
|
645
645
|
Parameters
|
|
646
646
|
----------
|
|
647
|
-
scada_data : :class:`~epyt_flow.scada.
|
|
647
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
648
648
|
The SCADA data object containing node data. If `None`, a simulation
|
|
649
649
|
will be run to generate SCADA data. Default is `None`.
|
|
650
650
|
parameter : `str`, optional
|
|
@@ -761,7 +761,7 @@ class ScenarioVisualizer:
|
|
|
761
761
|
|
|
762
762
|
Parameters
|
|
763
763
|
----------
|
|
764
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
764
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
765
765
|
The SCADA data object. If `None`, the method will run a simulation.
|
|
766
766
|
Default is `None`.
|
|
767
767
|
parameter : `str`, optional
|
|
@@ -861,7 +861,7 @@ class ScenarioVisualizer:
|
|
|
861
861
|
|
|
862
862
|
Parameters
|
|
863
863
|
----------
|
|
864
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
864
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
865
865
|
The SCADA data object containing the pump data. If `None`, a
|
|
866
866
|
simulation will be run to generate SCADA data. Default is `None`.
|
|
867
867
|
parameter : `str`, optional
|
|
@@ -971,7 +971,7 @@ class ScenarioVisualizer:
|
|
|
971
971
|
|
|
972
972
|
Parameters
|
|
973
973
|
----------
|
|
974
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
974
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
975
975
|
The SCADA data object containing tank volume data.
|
|
976
976
|
If `None`, a simulation will be run to generate it.
|
|
977
977
|
Default is `None`.
|
|
@@ -1066,7 +1066,7 @@ class ScenarioVisualizer:
|
|
|
1066
1066
|
|
|
1067
1067
|
Parameters
|
|
1068
1068
|
----------
|
|
1069
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
1069
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
1070
1070
|
The SCADA data object containing valve state data. If `None`, a
|
|
1071
1071
|
simulation is run to generate SCADA data. Default is `None`.
|
|
1072
1072
|
statistic : `str`, optional
|
|
@@ -1162,7 +1162,7 @@ class ScenarioVisualizer:
|
|
|
1162
1162
|
|
|
1163
1163
|
Parameters
|
|
1164
1164
|
----------
|
|
1165
|
-
scada_data : :class:`~epyt_flow.scada.scada_data.ScadaData`, optional
|
|
1165
|
+
scada_data : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`, optional
|
|
1166
1166
|
The SCADA data object. If `None`, a simulation will be run to
|
|
1167
1167
|
generate it. Default is `None`.
|
|
1168
1168
|
parameter : `str`, optional
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Module provides a class for implementing sensor configurations.
|
|
3
3
|
"""
|
|
4
4
|
from copy import deepcopy
|
|
5
|
-
import warnings
|
|
6
5
|
import itertools
|
|
7
6
|
import numpy as np
|
|
8
7
|
import epyt
|
|
@@ -465,7 +464,7 @@ class SensorConfig(JsonSerializable):
|
|
|
465
464
|
"""
|
|
466
465
|
def __init__(self, nodes: list[str], links: list[str], valves: list[str], pumps: list[str],
|
|
467
466
|
tanks: list[str], bulk_species: list[str], surface_species: list[str],
|
|
468
|
-
flow_unit: int
|
|
467
|
+
flow_unit: int,
|
|
469
468
|
pressure_sensors: list[str] = [],
|
|
470
469
|
flow_sensors: list[str] = [],
|
|
471
470
|
demand_sensors: list[str] = [],
|
|
@@ -685,16 +684,11 @@ class SensorConfig(JsonSerializable):
|
|
|
685
684
|
if any(s not in surface_species for s in surfacespecies_id_to_idx.keys()):
|
|
686
685
|
raise ValueError("Unknown surface species ID in 'surfacespecies_id_to_idx'")
|
|
687
686
|
|
|
688
|
-
if flow_unit
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
raise ValueError("Invalid value of 'flow_unit'")
|
|
694
|
-
else:
|
|
695
|
-
warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
|
|
696
|
-
" -- support of such old files will be removed in the next release!",
|
|
697
|
-
DeprecationWarning)
|
|
687
|
+
if not isinstance(flow_unit, int):
|
|
688
|
+
raise TypeError("'flow_unit' must be a an instance of 'int' " +
|
|
689
|
+
f"but not of '{type(flow_unit)}'")
|
|
690
|
+
if flow_unit not in range(10):
|
|
691
|
+
raise ValueError("Invalid value of 'flow_unit'")
|
|
698
692
|
|
|
699
693
|
if quality_unit is not None:
|
|
700
694
|
if not isinstance(quality_unit, int):
|
|
@@ -1781,12 +1775,12 @@ class SensorConfig(JsonSerializable):
|
|
|
1781
1775
|
@property
|
|
1782
1776
|
def sensors_id_to_idx(self) -> dict:
|
|
1783
1777
|
"""
|
|
1784
|
-
Gets a mapping of sensor IDs to indices in the final Numpy array returned by `get_data()`.
|
|
1778
|
+
Gets a mapping of sensor IDs to indices in the final `Numpy array <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_ returned by `get_data()`.
|
|
1785
1779
|
|
|
1786
1780
|
Returns
|
|
1787
1781
|
-------
|
|
1788
1782
|
`dict`
|
|
1789
|
-
Mapping of sensor IDs to indices in the final Numpy array.
|
|
1783
|
+
Mapping of sensor IDs to indices in the final `Numpy array <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_.
|
|
1790
1784
|
"""
|
|
1791
1785
|
return deepcopy(self.__sensors_id_to_idx)
|
|
1792
1786
|
|
|
@@ -1987,39 +1981,39 @@ class SensorConfig(JsonSerializable):
|
|
|
1987
1981
|
|
|
1988
1982
|
Parameters
|
|
1989
1983
|
----------
|
|
1990
|
-
pressures : `numpy.ndarray
|
|
1984
|
+
pressures : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1991
1985
|
Pressure values at all nodes.
|
|
1992
|
-
flows : `numpy.ndarray
|
|
1986
|
+
flows : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1993
1987
|
Flow values at all links/pipes.
|
|
1994
|
-
demands : `numpy.ndarray
|
|
1988
|
+
demands : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1995
1989
|
Demand values at all nodes.
|
|
1996
|
-
nodes_quality : `numpy.ndarray
|
|
1990
|
+
nodes_quality : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1997
1991
|
Quality values at all nodes.
|
|
1998
|
-
links_quality : `numpy.ndarray
|
|
1992
|
+
links_quality : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
1999
1993
|
Quality values at all links/pipes.
|
|
2000
|
-
pumps_state : `numpy.ndarray
|
|
1994
|
+
pumps_state : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2001
1995
|
States of all pumps.
|
|
2002
|
-
pumps_efficiency : `numpy.ndarray
|
|
1996
|
+
pumps_efficiency : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2003
1997
|
Efficiency of all pumps.
|
|
2004
|
-
pumps_energyconsumption : `numpy.ndarray
|
|
1998
|
+
pumps_energyconsumption : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2005
1999
|
Energy consumption of all pumps.
|
|
2006
|
-
valves_state : `numpy.ndarray
|
|
2000
|
+
valves_state : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2007
2001
|
States of all valves.
|
|
2008
|
-
tanks_volume : `numpy.ndarray
|
|
2002
|
+
tanks_volume : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2009
2003
|
Water volume in all tanks.
|
|
2010
|
-
bulk_species_node_concentrations : `numpy.ndarray
|
|
2004
|
+
bulk_species_node_concentrations : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2011
2005
|
Bulk species concentrations at all nodes.
|
|
2012
2006
|
|
|
2013
2007
|
Expect a three-dimensional array: First dimension denotes time,
|
|
2014
2008
|
second dimension corresponds to species ID,
|
|
2015
2009
|
and third dimension contains the concentration.
|
|
2016
|
-
bulk_species_link_concentrations : `numpy.ndarray
|
|
2010
|
+
bulk_species_link_concentrations : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2017
2011
|
Bulk species concentrations at all links/pipes.
|
|
2018
2012
|
|
|
2019
2013
|
Expect a three-dimensional array: First dimension denotes time,
|
|
2020
2014
|
second dimension corresponds to species ID,
|
|
2021
2015
|
and third dimension contains the concentration.
|
|
2022
|
-
surface_species_concentrations : `numpy.ndarray
|
|
2016
|
+
surface_species_concentrations : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2023
2017
|
Surface species concentrations at all links/pipes.
|
|
2024
2018
|
|
|
2025
2019
|
Expect a three-dimensional array: First dimension denotes time,
|
|
@@ -2028,7 +2022,7 @@ class SensorConfig(JsonSerializable):
|
|
|
2028
2022
|
|
|
2029
2023
|
Returns
|
|
2030
2024
|
-------
|
|
2031
|
-
`numpy.ndarray
|
|
2025
|
+
`numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
|
|
2032
2026
|
Sensor readings.
|
|
2033
2027
|
"""
|
|
2034
2028
|
data = []
|
epyt_flow/topology.py
CHANGED
|
@@ -552,7 +552,7 @@ class NetworkTopology(nx.Graph, JsonSerializable):
|
|
|
552
552
|
Returns
|
|
553
553
|
-------
|
|
554
554
|
`dict`
|
|
555
|
-
Network topology as a dictionary of `geopandas.GeoDataFrames
|
|
555
|
+
Network topology as a dictionary of `geopandas.GeoDataFrames <https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoDataFrame.html>`_ instances.
|
|
556
556
|
If a quantity does not exist, the data frame will be None.
|
|
557
557
|
"""
|
|
558
558
|
gis = {"nodes": None, "links": None,
|
|
@@ -629,7 +629,7 @@ class NetworkTopology(nx.Graph, JsonSerializable):
|
|
|
629
629
|
|
|
630
630
|
Returns
|
|
631
631
|
-------
|
|
632
|
-
`scipy.bsr_array
|
|
632
|
+
`scipy.bsr_array <https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.bsr_array.html>`_
|
|
633
633
|
Adjacency matrix as a sparse array.
|
|
634
634
|
"""
|
|
635
635
|
nodes_id = [node_id for node_id, _ in self.__nodes]
|