epyt-flow 0.7.0__py3-none-any.whl → 0.7.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epyt_flow/VERSION +1 -1
- epyt_flow/data/benchmarks/leakdb.py +1 -0
- epyt_flow/simulation/scada/scada_data.py +714 -15
- epyt_flow/simulation/scada/scada_data_export.py +5 -1
- epyt_flow/simulation/scenario_simulator.py +212 -92
- epyt_flow/simulation/sensor_config.py +48 -1
- epyt_flow/uncertainty/model_uncertainty.py +11 -5
- {epyt_flow-0.7.0.dist-info → epyt_flow-0.7.2.dist-info}/METADATA +2 -2
- {epyt_flow-0.7.0.dist-info → epyt_flow-0.7.2.dist-info}/RECORD +12 -12
- {epyt_flow-0.7.0.dist-info → epyt_flow-0.7.2.dist-info}/LICENSE +0 -0
- {epyt_flow-0.7.0.dist-info → epyt_flow-0.7.2.dist-info}/WHEEL +0 -0
- {epyt_flow-0.7.0.dist-info → epyt_flow-0.7.2.dist-info}/top_level.txt +0 -0
|
@@ -5,9 +5,11 @@ import warnings
|
|
|
5
5
|
from typing import Callable, Any
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
import numpy as np
|
|
8
|
+
import matplotlib
|
|
8
9
|
from epyt.epanet import ToolkitConstants
|
|
9
10
|
|
|
10
|
-
from ..sensor_config import SensorConfig, is_flowunit_simetric, massunit_to_str,
|
|
11
|
+
from ..sensor_config import SensorConfig, is_flowunit_simetric, massunit_to_str, flowunit_to_str,\
|
|
12
|
+
qualityunit_to_str, areaunit_to_str,\
|
|
11
13
|
MASS_UNIT_MG, MASS_UNIT_UG, TIME_UNIT_HRS, MASS_UNIT_MOL, MASS_UNIT_MMOL, \
|
|
12
14
|
AREA_UNIT_CM2, AREA_UNIT_FT2, AREA_UNIT_M2, \
|
|
13
15
|
SENSOR_TYPE_LINK_FLOW, SENSOR_TYPE_LINK_QUALITY, SENSOR_TYPE_NODE_DEMAND, \
|
|
@@ -18,6 +20,7 @@ from ..sensor_config import SensorConfig, is_flowunit_simetric, massunit_to_str,
|
|
|
18
20
|
from ..events import SensorFault, SensorReadingAttack, SensorReadingEvent
|
|
19
21
|
from ...uncertainty import SensorNoise
|
|
20
22
|
from ...serialization import serializable, Serializable, SCADA_DATA_ID
|
|
23
|
+
from ...utils import plot_timeseries_data
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
@serializable(SCADA_DATA_ID, ".epytflow_scada_data")
|
|
@@ -312,11 +315,11 @@ class ScadaData(Serializable):
|
|
|
312
315
|
else:
|
|
313
316
|
sensor_config = self.__sensor_config
|
|
314
317
|
|
|
315
|
-
node_to_idx = sensor_config.
|
|
316
|
-
link_to_idx = sensor_config.
|
|
317
|
-
pump_to_idx = sensor_config.
|
|
318
|
-
valve_to_idx = sensor_config.
|
|
319
|
-
tank_to_idx = sensor_config.
|
|
318
|
+
node_to_idx = sensor_config.map_node_id_to_idx
|
|
319
|
+
link_to_idx = sensor_config.map_link_id_to_idx
|
|
320
|
+
pump_to_idx = sensor_config.map_pump_id_to_idx
|
|
321
|
+
valve_to_idx = sensor_config.map_valve_id_to_idx
|
|
322
|
+
tank_to_idx = sensor_config.map_tank_id_to_idx
|
|
320
323
|
|
|
321
324
|
# EPANET quantities
|
|
322
325
|
def __reduce_data(data: np.ndarray, sensors: list[str],
|
|
@@ -349,7 +352,7 @@ class ScadaData(Serializable):
|
|
|
349
352
|
self.__pumps_energy_usage_data_raw = \
|
|
350
353
|
__reduce_data(data=pumps_energy_usage_data_raw,
|
|
351
354
|
item_to_idx=pump_to_idx,
|
|
352
|
-
sensors=sensor_config.
|
|
355
|
+
sensors=sensor_config.pump_energyconsumption_sensors)
|
|
353
356
|
self.__pumps_efficiency_data_raw = \
|
|
354
357
|
__reduce_data(data=pumps_efficiency_data_raw,
|
|
355
358
|
item_to_idx=pump_to_idx,
|
|
@@ -373,24 +376,24 @@ class ScadaData(Serializable):
|
|
|
373
376
|
|
|
374
377
|
return np.concatenate(r, axis=1)
|
|
375
378
|
|
|
376
|
-
node_bulk_species_idx = [(sensor_config.
|
|
377
|
-
[sensor_config.
|
|
379
|
+
node_bulk_species_idx = [(sensor_config.map_bulkspecies_id_to_idx(s),
|
|
380
|
+
[sensor_config.map_node_id_to_idx(node_id)
|
|
378
381
|
for node_id in sensor_config.bulk_species_node_sensors[s]
|
|
379
382
|
]) for s in sensor_config.bulk_species_node_sensors.keys()]
|
|
380
383
|
self.__bulk_species_node_concentration_raw = \
|
|
381
384
|
__reduce_msx_data(data=bulk_species_node_concentration_raw,
|
|
382
385
|
sensors=node_bulk_species_idx)
|
|
383
386
|
|
|
384
|
-
bulk_species_link_idx = [(sensor_config.
|
|
385
|
-
[sensor_config.
|
|
387
|
+
bulk_species_link_idx = [(sensor_config.map_bulkspecies_id_to_idx(s),
|
|
388
|
+
[sensor_config.map_link_id_to_idx(link_id)
|
|
386
389
|
for link_id in sensor_config.bulk_species_link_sensors[s]
|
|
387
390
|
]) for s in sensor_config.bulk_species_link_sensors.keys()]
|
|
388
391
|
self.__bulk_species_link_concentration_raw = \
|
|
389
392
|
__reduce_msx_data(data=bulk_species_link_concentration_raw,
|
|
390
393
|
sensors=bulk_species_link_idx)
|
|
391
394
|
|
|
392
|
-
surface_species_idx = [(sensor_config.
|
|
393
|
-
[sensor_config.
|
|
395
|
+
surface_species_idx = [(sensor_config.map_surfacespecies_id_to_idx(s),
|
|
396
|
+
[sensor_config.map_link_id_to_idx(link_id)
|
|
394
397
|
for link_id in sensor_config.surface_species_sensors[s]
|
|
395
398
|
]) for s in sensor_config.surface_species_sensors.keys()]
|
|
396
399
|
self.__surface_species_concentration_raw = \
|
|
@@ -1503,8 +1506,8 @@ class ScadaData(Serializable):
|
|
|
1503
1506
|
other.sensor_config.surface_species_sensors
|
|
1504
1507
|
|
|
1505
1508
|
if self.__pumps_energy_usage_data_raw is None and \
|
|
1506
|
-
other.
|
|
1507
|
-
self.__pumps_energy_usage_data_raw = other.
|
|
1509
|
+
other.pumps_energyconsumption_data_raw is not None:
|
|
1510
|
+
self.__pumps_energy_usage_data_raw = other.pumps_energyconsumption_data_raw
|
|
1508
1511
|
|
|
1509
1512
|
if self.__pumps_efficiency_data_raw is None and \
|
|
1510
1513
|
other.pumps_efficiency_data_raw is not None:
|
|
@@ -1684,6 +1687,19 @@ class ScadaData(Serializable):
|
|
|
1684
1687
|
|
|
1685
1688
|
return sensor_readings
|
|
1686
1689
|
|
|
1690
|
+
def __get_x_axis_label(self) -> str:
|
|
1691
|
+
if len(self.__sensor_readings_time) > 1:
|
|
1692
|
+
time_step = self.__sensor_readings_time[1] - self.__sensor_readings_time[0]
|
|
1693
|
+
if time_step > 60:
|
|
1694
|
+
time_steps_desc = f"{int(time_step / 60)}min"
|
|
1695
|
+
if time_step > 60*60:
|
|
1696
|
+
time_steps_desc = f"{int(time_step / 60)}hr"
|
|
1697
|
+
else:
|
|
1698
|
+
time_steps_desc = f"{time_step}s"
|
|
1699
|
+
return f"Time ({time_steps_desc} steps)"
|
|
1700
|
+
else:
|
|
1701
|
+
return "Time"
|
|
1702
|
+
|
|
1687
1703
|
def get_data_pressures(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1688
1704
|
"""
|
|
1689
1705
|
Gets the final pressure sensor readings -- note that those might be subject to
|
|
@@ -1722,6 +1738,55 @@ class ScadaData(Serializable):
|
|
|
1722
1738
|
for s_id in sensor_locations]
|
|
1723
1739
|
return self.__sensor_readings[:, idx]
|
|
1724
1740
|
|
|
1741
|
+
def plot_pressures(self, sensor_locations: list[str] = None, show: bool = True,
|
|
1742
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
1743
|
+
) -> matplotlib.axes.Axes:
|
|
1744
|
+
"""
|
|
1745
|
+
Plots the final pressure sensor readings -- note that those might be subject to
|
|
1746
|
+
given sensor faults and sensor noise/uncertainty.
|
|
1747
|
+
|
|
1748
|
+
Parameters
|
|
1749
|
+
----------
|
|
1750
|
+
sensor_locations : `list[str]`, optional
|
|
1751
|
+
Existing pressure sensor locations for which the sensor readings have to be plotted.
|
|
1752
|
+
If None, the readings from all pressure sensors are plotted.
|
|
1753
|
+
|
|
1754
|
+
The default is None.
|
|
1755
|
+
show : `bool`, optional
|
|
1756
|
+
If True, the plot/figure is shown in a window.
|
|
1757
|
+
|
|
1758
|
+
Only considered when 'ax' is None.
|
|
1759
|
+
|
|
1760
|
+
The default is True.
|
|
1761
|
+
save_to_file : `str`, optional
|
|
1762
|
+
File to which the plot is saved.
|
|
1763
|
+
|
|
1764
|
+
If specified, 'show' must be set to False --
|
|
1765
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
1766
|
+
|
|
1767
|
+
The default is None.
|
|
1768
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
1769
|
+
If not None, 'ax' is used for plotting.
|
|
1770
|
+
|
|
1771
|
+
The default is None.
|
|
1772
|
+
|
|
1773
|
+
Returns
|
|
1774
|
+
-------
|
|
1775
|
+
`matplotlib.axes.Axes`
|
|
1776
|
+
Plot.
|
|
1777
|
+
"""
|
|
1778
|
+
data = self.get_data_pressures(sensor_locations)
|
|
1779
|
+
pressure_sensors = sensor_locations if sensor_locations is not None else \
|
|
1780
|
+
self.__sensor_config.pressure_sensors
|
|
1781
|
+
|
|
1782
|
+
pressure_unit = "m" if is_flowunit_simetric(self.__sensor_config.flow_unit) else "psi"
|
|
1783
|
+
y_axis_label = f"Pressure in ${pressure_unit}$"
|
|
1784
|
+
|
|
1785
|
+
return plot_timeseries_data(data.T, labels=[f"Node {n_id}" for n_id in pressure_sensors],
|
|
1786
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
1787
|
+
y_axis_label=y_axis_label,
|
|
1788
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
1789
|
+
|
|
1725
1790
|
def get_data_flows(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1726
1791
|
"""
|
|
1727
1792
|
Gets the final flow sensor readings -- note that those might be subject to
|
|
@@ -1760,6 +1825,54 @@ class ScadaData(Serializable):
|
|
|
1760
1825
|
for s_id in sensor_locations]
|
|
1761
1826
|
return self.__sensor_readings[:, idx]
|
|
1762
1827
|
|
|
1828
|
+
def plot_flows(self, sensor_locations: list[str] = None, show: bool = True,
|
|
1829
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
1830
|
+
) -> matplotlib.axes.Axes:
|
|
1831
|
+
"""
|
|
1832
|
+
Plots the final flow sensor readings -- note that those might be subject to
|
|
1833
|
+
given sensor faults and sensor noise/uncertainty.
|
|
1834
|
+
|
|
1835
|
+
Parameters
|
|
1836
|
+
----------
|
|
1837
|
+
sensor_locations : `list[str]`, optional
|
|
1838
|
+
Existing flow sensor locations for which the sensor readings have to be plotted.
|
|
1839
|
+
If None, the readings from all flow sensors are plotted.
|
|
1840
|
+
|
|
1841
|
+
The default is None.
|
|
1842
|
+
show : `bool`, optional
|
|
1843
|
+
If True, the plot/figure is shown in a window.
|
|
1844
|
+
|
|
1845
|
+
Only considered when 'ax' is None.
|
|
1846
|
+
|
|
1847
|
+
The default is True.
|
|
1848
|
+
save_to_file : `str`, optional
|
|
1849
|
+
File to which the plot is saved.
|
|
1850
|
+
|
|
1851
|
+
If specified, 'show' must be set to False --
|
|
1852
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
1853
|
+
|
|
1854
|
+
The default is None.
|
|
1855
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
1856
|
+
If not None, 'ax' is used for plotting.
|
|
1857
|
+
|
|
1858
|
+
The default is None.
|
|
1859
|
+
|
|
1860
|
+
Returns
|
|
1861
|
+
-------
|
|
1862
|
+
`matplotlib.axes.Axes`
|
|
1863
|
+
Plot.
|
|
1864
|
+
"""
|
|
1865
|
+
data = self.get_data_flows(sensor_locations)
|
|
1866
|
+
flow_sensors = sensor_locations if sensor_locations is not None else \
|
|
1867
|
+
self.__sensor_config.flow_sensors
|
|
1868
|
+
|
|
1869
|
+
y_axis_label = f"Flow rate in ${flowunit_to_str(self.__sensor_config.flow_unit)}$"
|
|
1870
|
+
|
|
1871
|
+
return plot_timeseries_data(data.T, labels=[f"Link {n_id}" for n_id in flow_sensors],
|
|
1872
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
1873
|
+
y_axis_label=y_axis_label,
|
|
1874
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
1875
|
+
|
|
1763
1876
|
def get_data_demands(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1764
1877
|
"""
|
|
1765
1878
|
Gets the final demand sensor readings -- note that those might be subject to
|
|
@@ -1798,6 +1911,54 @@ class ScadaData(Serializable):
|
|
|
1798
1911
|
for s_id in sensor_locations]
|
|
1799
1912
|
return self.__sensor_readings[:, idx]
|
|
1800
1913
|
|
|
1914
|
+
def plot_demands(self, sensor_locations: list[str] = None, show: bool = True,
|
|
1915
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
1916
|
+
) -> matplotlib.axes.Axes:
|
|
1917
|
+
"""
|
|
1918
|
+
Plots the final demand sensor readings -- note that those might be subject to
|
|
1919
|
+
given sensor faults and sensor noise/uncertainty.
|
|
1920
|
+
|
|
1921
|
+
Parameters
|
|
1922
|
+
----------
|
|
1923
|
+
sensor_locations : `list[str]`, optional
|
|
1924
|
+
Existing demand sensor locations for which the sensor readings have to be plotted.
|
|
1925
|
+
If None, the readings from all demand sensors are plotted.
|
|
1926
|
+
|
|
1927
|
+
The default is None.
|
|
1928
|
+
show : `bool`, optional
|
|
1929
|
+
If True, the plot/figure is shown in a window.
|
|
1930
|
+
|
|
1931
|
+
Only considered when 'ax' is None.
|
|
1932
|
+
|
|
1933
|
+
The default is True.
|
|
1934
|
+
save_to_file : `str`, optional
|
|
1935
|
+
File to which the plot is saved.
|
|
1936
|
+
|
|
1937
|
+
If specified, 'show' must be set to False --
|
|
1938
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
1939
|
+
|
|
1940
|
+
The default is None.
|
|
1941
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
1942
|
+
If not None, 'ax' is used for plotting.
|
|
1943
|
+
|
|
1944
|
+
The default is None.
|
|
1945
|
+
|
|
1946
|
+
Returns
|
|
1947
|
+
-------
|
|
1948
|
+
`matplotlib.axes.Axes`
|
|
1949
|
+
Plot.
|
|
1950
|
+
"""
|
|
1951
|
+
data = self.get_data_demands(sensor_locations)
|
|
1952
|
+
demand_sensors = sensor_locations if sensor_locations is not None else \
|
|
1953
|
+
self.__sensor_config.demand_sensors
|
|
1954
|
+
|
|
1955
|
+
y_axis_label = f"Demand in ${flowunit_to_str(self.__sensor_config.flow_unit)}$"
|
|
1956
|
+
|
|
1957
|
+
return plot_timeseries_data(data.T, labels=[f"Node {n_id}" for n_id in demand_sensors],
|
|
1958
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
1959
|
+
y_axis_label=y_axis_label,
|
|
1960
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
1961
|
+
|
|
1801
1962
|
def get_data_nodes_quality(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1802
1963
|
"""
|
|
1803
1964
|
Gets the final node quality sensor readings -- note that those might be subject to
|
|
@@ -1837,6 +1998,56 @@ class ScadaData(Serializable):
|
|
|
1837
1998
|
for s_id in sensor_locations]
|
|
1838
1999
|
return self.__sensor_readings[:, idx]
|
|
1839
2000
|
|
|
2001
|
+
def plot_nodes_quality(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2002
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2003
|
+
) -> matplotlib.axes.Axes:
|
|
2004
|
+
"""
|
|
2005
|
+
Plots the final node quality sensor readings -- note that those might be subject to
|
|
2006
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2007
|
+
|
|
2008
|
+
Parameters
|
|
2009
|
+
----------
|
|
2010
|
+
sensor_locations : `list[str]`, optional
|
|
2011
|
+
Existing node quality sensor locations for which the sensor readings
|
|
2012
|
+
have to be plotted.
|
|
2013
|
+
If None, the readings from all node quality sensors are plotted.
|
|
2014
|
+
|
|
2015
|
+
The default is None.
|
|
2016
|
+
show : `bool`, optional
|
|
2017
|
+
If True, the plot/figure is shown in a window.
|
|
2018
|
+
|
|
2019
|
+
Only considered when 'ax' is None.
|
|
2020
|
+
|
|
2021
|
+
The default is True.
|
|
2022
|
+
save_to_file : `str`, optional
|
|
2023
|
+
File to which the plot is saved.
|
|
2024
|
+
|
|
2025
|
+
If specified, 'show' must be set to False --
|
|
2026
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2027
|
+
|
|
2028
|
+
The default is None.
|
|
2029
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2030
|
+
If not None, 'ax' is used for plotting.
|
|
2031
|
+
|
|
2032
|
+
The default is None.
|
|
2033
|
+
|
|
2034
|
+
Returns
|
|
2035
|
+
-------
|
|
2036
|
+
`matplotlib.axes.Axes`
|
|
2037
|
+
Plot.
|
|
2038
|
+
"""
|
|
2039
|
+
data = self.get_data_nodes_quality(sensor_locations)
|
|
2040
|
+
nodes_quality_sensors = sensor_locations if sensor_locations is not None else \
|
|
2041
|
+
self.__sensor_config.quality_node_sensors
|
|
2042
|
+
|
|
2043
|
+
y_axis_label = f"${qualityunit_to_str(self.__sensor_config.quality_unit)}$"
|
|
2044
|
+
|
|
2045
|
+
return plot_timeseries_data(data.T, labels=[f"Node {n_id}"
|
|
2046
|
+
for n_id in nodes_quality_sensors],
|
|
2047
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2048
|
+
y_axis_label=y_axis_label,
|
|
2049
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2050
|
+
|
|
1840
2051
|
def get_data_links_quality(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1841
2052
|
"""
|
|
1842
2053
|
Gets the final link quality sensor readings -- note that those might be subject to
|
|
@@ -1876,6 +2087,56 @@ class ScadaData(Serializable):
|
|
|
1876
2087
|
for s_id in sensor_locations]
|
|
1877
2088
|
return self.__sensor_readings[:, idx]
|
|
1878
2089
|
|
|
2090
|
+
def plot_links_quality(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2091
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2092
|
+
) -> matplotlib.axes.Axes:
|
|
2093
|
+
"""
|
|
2094
|
+
Plots the final link/pipe quality sensor readings -- note that those might be subject to
|
|
2095
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2096
|
+
|
|
2097
|
+
Parameters
|
|
2098
|
+
----------
|
|
2099
|
+
sensor_locations : `list[str]`, optional
|
|
2100
|
+
Existing link quality sensor locations for which the sensor readings
|
|
2101
|
+
have to be plotted.
|
|
2102
|
+
If None, the readings from all link quality sensors are plotted.
|
|
2103
|
+
|
|
2104
|
+
The default is None.
|
|
2105
|
+
show : `bool`, optional
|
|
2106
|
+
If True, the plot/figure is shown in a window.
|
|
2107
|
+
|
|
2108
|
+
Only considered when 'ax' is None.
|
|
2109
|
+
|
|
2110
|
+
The default is True.
|
|
2111
|
+
save_to_file : `str`, optional
|
|
2112
|
+
File to which the plot is saved.
|
|
2113
|
+
|
|
2114
|
+
If specified, 'show' must be set to False --
|
|
2115
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2116
|
+
|
|
2117
|
+
The default is None.
|
|
2118
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2119
|
+
If not None, 'ax' is used for plotting.
|
|
2120
|
+
|
|
2121
|
+
The default is None.
|
|
2122
|
+
|
|
2123
|
+
Returns
|
|
2124
|
+
-------
|
|
2125
|
+
`matplotlib.axes.Axes`
|
|
2126
|
+
Plot.
|
|
2127
|
+
"""
|
|
2128
|
+
data = self.get_data_links_quality(sensor_locations)
|
|
2129
|
+
links_quality_sensors = sensor_locations if sensor_locations is not None else \
|
|
2130
|
+
self.__sensor_config.quality_link_sensors
|
|
2131
|
+
|
|
2132
|
+
y_axis_label = f"${qualityunit_to_str(self.__sensor_config.quality_unit)}$"
|
|
2133
|
+
|
|
2134
|
+
return plot_timeseries_data(data.T, labels=[f"Link {n_id}"
|
|
2135
|
+
for n_id in links_quality_sensors],
|
|
2136
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2137
|
+
y_axis_label=y_axis_label,
|
|
2138
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2139
|
+
|
|
1879
2140
|
def get_data_pumps_state(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1880
2141
|
"""
|
|
1881
2142
|
Gets the final pump state sensor readings -- note that those might be subject to
|
|
@@ -1915,6 +2176,54 @@ class ScadaData(Serializable):
|
|
|
1915
2176
|
for s_id in sensor_locations]
|
|
1916
2177
|
return self.__sensor_readings[:, idx]
|
|
1917
2178
|
|
|
2179
|
+
def plot_pumps_state(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2180
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2181
|
+
) -> matplotlib.axes.Axes:
|
|
2182
|
+
"""
|
|
2183
|
+
Plots the final pump state sensor readings -- note that those might be subject to
|
|
2184
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2185
|
+
|
|
2186
|
+
Parameters
|
|
2187
|
+
----------
|
|
2188
|
+
sensor_locations : `list[str]`, optional
|
|
2189
|
+
Existing pump state sensor locations for which the sensor readings have to be plotted.
|
|
2190
|
+
If None, the readings from all pump state sensors are plotted.
|
|
2191
|
+
|
|
2192
|
+
The default is None.
|
|
2193
|
+
show : `bool`, optional
|
|
2194
|
+
If True, the plot/figure is shown in a window.
|
|
2195
|
+
|
|
2196
|
+
Only considered when 'ax' is None.
|
|
2197
|
+
|
|
2198
|
+
The default is True.
|
|
2199
|
+
save_to_file : `str`, optional
|
|
2200
|
+
File to which the plot is saved.
|
|
2201
|
+
|
|
2202
|
+
If specified, 'show' must be set to False --
|
|
2203
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2204
|
+
|
|
2205
|
+
The default is None.
|
|
2206
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2207
|
+
If not None, 'ax' is used for plotting.
|
|
2208
|
+
|
|
2209
|
+
The default is None.
|
|
2210
|
+
|
|
2211
|
+
Returns
|
|
2212
|
+
-------
|
|
2213
|
+
`matplotlib.axes.Axes`
|
|
2214
|
+
Plot.
|
|
2215
|
+
"""
|
|
2216
|
+
data = self.get_data_pumps_state(sensor_locations)
|
|
2217
|
+
pump_state_sensors = sensor_locations if sensor_locations is not None else \
|
|
2218
|
+
self.__sensor_config.pump_state_sensors
|
|
2219
|
+
|
|
2220
|
+
return plot_timeseries_data(data.T, labels=[f"Pump {n_id}"
|
|
2221
|
+
for n_id in pump_state_sensors],
|
|
2222
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2223
|
+
y_axis_label="Pump state",
|
|
2224
|
+
y_ticks=([2.0, 3.0], ["Off", "On"]),
|
|
2225
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2226
|
+
|
|
1918
2227
|
def get_data_pumps_efficiency(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1919
2228
|
"""
|
|
1920
2229
|
Gets the final pump efficiency sensor readings -- note that those might be subject to
|
|
@@ -1954,6 +2263,54 @@ class ScadaData(Serializable):
|
|
|
1954
2263
|
for s_id in sensor_locations]
|
|
1955
2264
|
return self.__sensor_readings[:, idx]
|
|
1956
2265
|
|
|
2266
|
+
def plot_pumps_efficiency(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2267
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2268
|
+
) -> matplotlib.axes.Axes:
|
|
2269
|
+
"""
|
|
2270
|
+
Plots the final pump efficiency sensor readings -- note that those might be subject to
|
|
2271
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2272
|
+
|
|
2273
|
+
Parameters
|
|
2274
|
+
----------
|
|
2275
|
+
sensor_locations : `list[str]`, optional
|
|
2276
|
+
Existing pump efficiency sensor locations for which the sensor readings
|
|
2277
|
+
have to be plotted.
|
|
2278
|
+
If None, the readings from all pump efficiency sensors are plotted.
|
|
2279
|
+
|
|
2280
|
+
The default is None.
|
|
2281
|
+
show : `bool`, optional
|
|
2282
|
+
If True, the plot/figure is shown in a window.
|
|
2283
|
+
|
|
2284
|
+
Only considered when 'ax' is None.
|
|
2285
|
+
|
|
2286
|
+
The default is True.
|
|
2287
|
+
save_to_file : `str`, optional
|
|
2288
|
+
File to which the plot is saved.
|
|
2289
|
+
|
|
2290
|
+
If specified, 'show' must be set to False --
|
|
2291
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2292
|
+
|
|
2293
|
+
The default is None.
|
|
2294
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2295
|
+
If not None, 'ax' is used for plotting.
|
|
2296
|
+
|
|
2297
|
+
The default is None.
|
|
2298
|
+
|
|
2299
|
+
Returns
|
|
2300
|
+
-------
|
|
2301
|
+
`matplotlib.axes.Axes`
|
|
2302
|
+
Plot.
|
|
2303
|
+
"""
|
|
2304
|
+
data = self.get_data_pumps_efficiency(sensor_locations)
|
|
2305
|
+
pump_efficiency_sensors = sensor_locations if sensor_locations is not None else \
|
|
2306
|
+
self.__sensor_config.pump_efficiency_sensors
|
|
2307
|
+
|
|
2308
|
+
return plot_timeseries_data(data.T, labels=[f"Pump {n_id}"
|
|
2309
|
+
for n_id in pump_efficiency_sensors],
|
|
2310
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2311
|
+
y_axis_label="Efficiency in $\%$",
|
|
2312
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2313
|
+
|
|
1957
2314
|
def get_data_pumps_energyconsumption(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1958
2315
|
"""
|
|
1959
2316
|
Gets the final pump energy consumption sensor readings -- note that those might be subject
|
|
@@ -1994,6 +2351,54 @@ class ScadaData(Serializable):
|
|
|
1994
2351
|
for s_id in sensor_locations]
|
|
1995
2352
|
return self.__sensor_readings[:, idx]
|
|
1996
2353
|
|
|
2354
|
+
def plot_pumps_energyconsumption(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2355
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2356
|
+
) -> matplotlib.axes.Axes:
|
|
2357
|
+
"""
|
|
2358
|
+
Plots the final pump energy consumption sensor readings -- note that those might be
|
|
2359
|
+
subject to given sensor faults and sensor noise/uncertainty.
|
|
2360
|
+
|
|
2361
|
+
Parameters
|
|
2362
|
+
----------
|
|
2363
|
+
sensor_locations : `list[str]`, optional
|
|
2364
|
+
Existing pump energy consumption sensor locations for which the sensor readings
|
|
2365
|
+
have to be plotted.
|
|
2366
|
+
If None, the readings from all pump energy consumption sensors are plotted.
|
|
2367
|
+
|
|
2368
|
+
The default is None.
|
|
2369
|
+
show : `bool`, optional
|
|
2370
|
+
If True, the plot/figure is shown in a window.
|
|
2371
|
+
|
|
2372
|
+
Only considered when 'ax' is None.
|
|
2373
|
+
|
|
2374
|
+
The default is True.
|
|
2375
|
+
save_to_file : `str`, optional
|
|
2376
|
+
File to which the plot is saved.
|
|
2377
|
+
|
|
2378
|
+
If specified, 'show' must be set to False --
|
|
2379
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2380
|
+
|
|
2381
|
+
The default is None.
|
|
2382
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2383
|
+
If not None, 'ax' is used for plotting.
|
|
2384
|
+
|
|
2385
|
+
The default is None.
|
|
2386
|
+
|
|
2387
|
+
Returns
|
|
2388
|
+
-------
|
|
2389
|
+
`matplotlib.axes.Axes`
|
|
2390
|
+
Plot.
|
|
2391
|
+
"""
|
|
2392
|
+
data = self.get_data_pumps_energyconsumption(sensor_locations)
|
|
2393
|
+
pump_energyconsumption_sensors = sensor_locations if sensor_locations is not None else \
|
|
2394
|
+
self.__sensor_config.pump_energyconsumption_sensors
|
|
2395
|
+
|
|
2396
|
+
return plot_timeseries_data(data.T, labels=[f"Pump {n_id}"
|
|
2397
|
+
for n_id in pump_energyconsumption_sensors],
|
|
2398
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2399
|
+
y_axis_label="Energy consumption in $kilowatt - hour$",
|
|
2400
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2401
|
+
|
|
1997
2402
|
def get_data_valves_state(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
1998
2403
|
"""
|
|
1999
2404
|
Gets the final valve state sensor readings -- note that those might be subject to
|
|
@@ -2033,6 +2438,54 @@ class ScadaData(Serializable):
|
|
|
2033
2438
|
for s_id in sensor_locations]
|
|
2034
2439
|
return self.__sensor_readings[:, idx]
|
|
2035
2440
|
|
|
2441
|
+
def plot_valves_state(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2442
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2443
|
+
) -> matplotlib.axes.Axes:
|
|
2444
|
+
"""
|
|
2445
|
+
Plots the final valve state sensor readings -- note that those might be subject to
|
|
2446
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2447
|
+
|
|
2448
|
+
Parameters
|
|
2449
|
+
----------
|
|
2450
|
+
sensor_locations : `list[str]`, optional
|
|
2451
|
+
Existing valve state sensor locations for which the sensor readings have to be plotted.
|
|
2452
|
+
If None, the readings from all valve state sensors are plotted.
|
|
2453
|
+
|
|
2454
|
+
The default is None.
|
|
2455
|
+
show : `bool`, optional
|
|
2456
|
+
If True, the plot/figure is shown in a window.
|
|
2457
|
+
|
|
2458
|
+
Only considered when 'ax' is None.
|
|
2459
|
+
|
|
2460
|
+
The default is True.
|
|
2461
|
+
save_to_file : `str`, optional
|
|
2462
|
+
File to which the plot is saved.
|
|
2463
|
+
|
|
2464
|
+
If specified, 'show' must be set to False --
|
|
2465
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2466
|
+
|
|
2467
|
+
The default is None.
|
|
2468
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2469
|
+
If not None, 'ax' is used for plotting.
|
|
2470
|
+
|
|
2471
|
+
The default is None.
|
|
2472
|
+
|
|
2473
|
+
Returns
|
|
2474
|
+
-------
|
|
2475
|
+
`matplotlib.axes.Axes`
|
|
2476
|
+
Plot.
|
|
2477
|
+
"""
|
|
2478
|
+
data = self.get_data_valves_state(sensor_locations)
|
|
2479
|
+
valve_state_sensors = sensor_locations if sensor_locations is not None else \
|
|
2480
|
+
self.__sensor_config.valve_state_sensors
|
|
2481
|
+
|
|
2482
|
+
return plot_timeseries_data(data.T, labels=[f"Valve {n_id}"
|
|
2483
|
+
for n_id in valve_state_sensors],
|
|
2484
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2485
|
+
y_axis_label="Valve state",
|
|
2486
|
+
y_ticks=([2.0, 3.0], ["Closed", "Open"]),
|
|
2487
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2488
|
+
|
|
2036
2489
|
def get_data_tanks_water_volume(self, sensor_locations: list[str] = None) -> np.ndarray:
|
|
2037
2490
|
"""
|
|
2038
2491
|
Gets the final water tanks volume sensor readings -- note that those might be subject to
|
|
@@ -2072,6 +2525,56 @@ class ScadaData(Serializable):
|
|
|
2072
2525
|
for s_id in sensor_locations]
|
|
2073
2526
|
return self.__sensor_readings[:, idx]
|
|
2074
2527
|
|
|
2528
|
+
def plot_tanks_water_volume(self, sensor_locations: list[str] = None, show: bool = True,
|
|
2529
|
+
save_to_file: str = None, ax: matplotlib.axes.Axes = None
|
|
2530
|
+
) -> matplotlib.axes.Axes:
|
|
2531
|
+
"""
|
|
2532
|
+
Plots the final water tanks volume sensor readings -- note that those might be subject to
|
|
2533
|
+
given sensor faults and sensor noise/uncertainty.
|
|
2534
|
+
|
|
2535
|
+
Parameters
|
|
2536
|
+
----------
|
|
2537
|
+
sensor_locations : `list[str]`, optional
|
|
2538
|
+
Existing flow sensor locations for which the sensor readings have to be plotted.
|
|
2539
|
+
If None, the readings from all water tanks volume sensors are plotted.
|
|
2540
|
+
|
|
2541
|
+
The default is None.
|
|
2542
|
+
show : `bool`, optional
|
|
2543
|
+
If True, the plot/figure is shown in a window.
|
|
2544
|
+
|
|
2545
|
+
Only considered when 'ax' is None.
|
|
2546
|
+
|
|
2547
|
+
The default is True.
|
|
2548
|
+
save_to_file : `str`, optional
|
|
2549
|
+
File to which the plot is saved.
|
|
2550
|
+
|
|
2551
|
+
If specified, 'show' must be set to False --
|
|
2552
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2553
|
+
|
|
2554
|
+
The default is None.
|
|
2555
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2556
|
+
If not None, 'ax' is used for plotting.
|
|
2557
|
+
|
|
2558
|
+
The default is None.
|
|
2559
|
+
|
|
2560
|
+
Returns
|
|
2561
|
+
-------
|
|
2562
|
+
`matplotlib.axes.Axes`
|
|
2563
|
+
Plot.
|
|
2564
|
+
"""
|
|
2565
|
+
data = self.get_data_tanks_water_volume(sensor_locations)
|
|
2566
|
+
tank_volume_sensors = sensor_locations if sensor_locations is not None else \
|
|
2567
|
+
self.__sensor_config.tank_volume_sensors
|
|
2568
|
+
|
|
2569
|
+
volume_unit = "m^3" if is_flowunit_simetric(self.__sensor_config.flow_unit) else "feet^3"
|
|
2570
|
+
y_axis_label = f"Water volume in ${volume_unit}$"
|
|
2571
|
+
|
|
2572
|
+
return plot_timeseries_data(data.T, labels=[f"Tank {n_id}"
|
|
2573
|
+
for n_id in tank_volume_sensors],
|
|
2574
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2575
|
+
y_axis_label=y_axis_label,
|
|
2576
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2577
|
+
|
|
2075
2578
|
def get_data_surface_species_concentration(self,
|
|
2076
2579
|
surface_species_sensor_locations: dict = None
|
|
2077
2580
|
) -> np.ndarray:
|
|
@@ -2122,6 +2625,72 @@ class ScadaData(Serializable):
|
|
|
2122
2625
|
for link_id in surface_species_sensor_locations[species_id]]
|
|
2123
2626
|
return self.__sensor_readings[:, idx]
|
|
2124
2627
|
|
|
2628
|
+
def plot_surface_species_concentration(self, surface_species_sensor_locations: dict = None,
|
|
2629
|
+
show: bool = True, save_to_file: str = None,
|
|
2630
|
+
ax: matplotlib.axes.Axes = None
|
|
2631
|
+
) -> matplotlib.axes.Axes:
|
|
2632
|
+
"""
|
|
2633
|
+
Plots the final surface species concentration sensor readings -- note that those might be
|
|
2634
|
+
subject to given sensor faults and sensor noise/uncertainty.
|
|
2635
|
+
|
|
2636
|
+
Parameters
|
|
2637
|
+
----------
|
|
2638
|
+
surface_species_sensor_locations : `dict`, optional
|
|
2639
|
+
Existing surface species concentration sensors (species ID and link/pipe IDs) for which
|
|
2640
|
+
the sensor readings have to be plotted.
|
|
2641
|
+
If None, the readings from all surface species concentration sensors are plotted.
|
|
2642
|
+
|
|
2643
|
+
The default is None.
|
|
2644
|
+
show : `bool`, optional
|
|
2645
|
+
If True, the plot/figure is shown in a window.
|
|
2646
|
+
|
|
2647
|
+
Only considered when 'ax' is None.
|
|
2648
|
+
|
|
2649
|
+
The default is True.
|
|
2650
|
+
save_to_file : `str`, optional
|
|
2651
|
+
File to which the plot is saved.
|
|
2652
|
+
|
|
2653
|
+
If specified, 'show' must be set to False --
|
|
2654
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2655
|
+
|
|
2656
|
+
The default is None.
|
|
2657
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2658
|
+
If not None, 'ax' is used for plotting.
|
|
2659
|
+
|
|
2660
|
+
The default is None.
|
|
2661
|
+
|
|
2662
|
+
Returns
|
|
2663
|
+
-------
|
|
2664
|
+
`matplotlib.axes.Axes`
|
|
2665
|
+
Plot.
|
|
2666
|
+
"""
|
|
2667
|
+
data = self.get_data_surface_species_concentration(surface_species_sensor_locations)
|
|
2668
|
+
if surface_species_sensor_locations is None:
|
|
2669
|
+
surface_species_sensor_locations = self.__sensor_config.surface_species_sensors
|
|
2670
|
+
|
|
2671
|
+
area_unit = self.__sensor_config.surface_species_area_unit
|
|
2672
|
+
concentration_unit = None
|
|
2673
|
+
labels = []
|
|
2674
|
+
for species_id in surface_species_sensor_locations:
|
|
2675
|
+
mass_unit = self.__sensor_config.get_surface_species_mass_unit_id(species_id)
|
|
2676
|
+
if concentration_unit is not None:
|
|
2677
|
+
if concentration_unit != mass_unit:
|
|
2678
|
+
raise ValueError("Can not plot species with different mass units")
|
|
2679
|
+
concentration_unit = mass_unit
|
|
2680
|
+
else:
|
|
2681
|
+
concentration_unit = mass_unit
|
|
2682
|
+
|
|
2683
|
+
for link_id in surface_species_sensor_locations[species_id]:
|
|
2684
|
+
labels.append(f"{species_id} @ link {link_id}")
|
|
2685
|
+
|
|
2686
|
+
y_axis_label = f"Concentration in ${massunit_to_str(concentration_unit)}/" +\
|
|
2687
|
+
f"{areaunit_to_str(area_unit)}$"
|
|
2688
|
+
|
|
2689
|
+
return plot_timeseries_data(data.T, labels=labels,
|
|
2690
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2691
|
+
y_axis_label=y_axis_label,
|
|
2692
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2693
|
+
|
|
2125
2694
|
def get_data_bulk_species_node_concentration(self,
|
|
2126
2695
|
bulk_species_sensor_locations: dict = None
|
|
2127
2696
|
) -> np.ndarray:
|
|
@@ -2172,6 +2741,70 @@ class ScadaData(Serializable):
|
|
|
2172
2741
|
for node_id in bulk_species_sensor_locations[species_id]]
|
|
2173
2742
|
return self.__sensor_readings[:, idx]
|
|
2174
2743
|
|
|
2744
|
+
def plot_bulk_species_node_concentration(self, bulk_species_node_sensors: dict = None,
|
|
2745
|
+
show: bool = True, save_to_file: str = None,
|
|
2746
|
+
ax: matplotlib.axes.Axes = None
|
|
2747
|
+
) -> matplotlib.axes.Axes:
|
|
2748
|
+
"""
|
|
2749
|
+
Plots the final bulk species node concentration sensor readings --
|
|
2750
|
+
note that those might be subject to given sensor faults and sensor noise/uncertainty.
|
|
2751
|
+
|
|
2752
|
+
Parameters
|
|
2753
|
+
----------
|
|
2754
|
+
bulk_species_node_sensors : `dict`, optional
|
|
2755
|
+
Existing bulk species concentration sensors (species ID and node IDs) for which
|
|
2756
|
+
the sensor readings are requested.
|
|
2757
|
+
If None, the readings from all bulk species node concentration sensors are returned.
|
|
2758
|
+
|
|
2759
|
+
The default is None.
|
|
2760
|
+
show : `bool`, optional
|
|
2761
|
+
If True, the plot/figure is shown in a window.
|
|
2762
|
+
|
|
2763
|
+
Only considered when 'ax' is None.
|
|
2764
|
+
|
|
2765
|
+
The default is True.
|
|
2766
|
+
save_to_file : `str`, optional
|
|
2767
|
+
File to which the plot is saved.
|
|
2768
|
+
|
|
2769
|
+
If specified, 'show' must be set to False --
|
|
2770
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2771
|
+
|
|
2772
|
+
The default is None.
|
|
2773
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2774
|
+
If not None, 'ax' is used for plotting.
|
|
2775
|
+
|
|
2776
|
+
The default is None.
|
|
2777
|
+
|
|
2778
|
+
Returns
|
|
2779
|
+
-------
|
|
2780
|
+
`matplotlib.axes.Axes`
|
|
2781
|
+
Plot.
|
|
2782
|
+
"""
|
|
2783
|
+
data = self.get_data_bulk_species_node_concentration(bulk_species_node_sensors)
|
|
2784
|
+
if bulk_species_node_sensors is None:
|
|
2785
|
+
bulk_species_node_sensors = self.__sensor_config.bulk_species_node_sensors
|
|
2786
|
+
|
|
2787
|
+
concentration_unit = None
|
|
2788
|
+
labels = []
|
|
2789
|
+
for species_id in bulk_species_node_sensors:
|
|
2790
|
+
mass_unit = self.__sensor_config.get_bulk_species_mass_unit_id(species_id)
|
|
2791
|
+
if concentration_unit is not None:
|
|
2792
|
+
if concentration_unit != mass_unit:
|
|
2793
|
+
raise ValueError("Can not plot species with different mass units")
|
|
2794
|
+
concentration_unit = mass_unit
|
|
2795
|
+
else:
|
|
2796
|
+
concentration_unit = mass_unit
|
|
2797
|
+
|
|
2798
|
+
for node_id in bulk_species_node_sensors[species_id]:
|
|
2799
|
+
labels.append(f"{species_id} @ node {node_id}")
|
|
2800
|
+
|
|
2801
|
+
y_axis_label = f"Concentration in ${massunit_to_str(concentration_unit)}/L$"
|
|
2802
|
+
|
|
2803
|
+
return plot_timeseries_data(data.T, labels=labels,
|
|
2804
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2805
|
+
y_axis_label=y_axis_label,
|
|
2806
|
+
show=show, save_to_file=save_to_file, ax=ax)
|
|
2807
|
+
|
|
2175
2808
|
def get_data_bulk_species_link_concentration(self,
|
|
2176
2809
|
bulk_species_sensor_locations: dict = None
|
|
2177
2810
|
) -> np.ndarray:
|
|
@@ -2222,3 +2855,69 @@ class ScadaData(Serializable):
|
|
|
2222
2855
|
for species_id in bulk_species_sensor_locations
|
|
2223
2856
|
for node_id in bulk_species_sensor_locations[species_id]]
|
|
2224
2857
|
return self.__sensor_readings[:, idx]
|
|
2858
|
+
|
|
2859
|
+
def plot_bulk_species_link_concentration(self, bulk_species_link_sensors: dict = None,
|
|
2860
|
+
show: bool = True, save_to_file: str = None,
|
|
2861
|
+
ax: matplotlib.axes.Axes = None
|
|
2862
|
+
) -> matplotlib.axes.Axes:
|
|
2863
|
+
"""
|
|
2864
|
+
Plots the final bulk species link concentration sensor readings -- note that those might be
|
|
2865
|
+
subject to given sensor faults and sensor noise/uncertainty.
|
|
2866
|
+
|
|
2867
|
+
Parameters
|
|
2868
|
+
----------
|
|
2869
|
+
bulk_species_link_sensors : `dict`, optional
|
|
2870
|
+
Existing bulk species link concentration sensors (species ID and link/pipe IDs) for which
|
|
2871
|
+
the sensor readings have to be plotted.
|
|
2872
|
+
If None, the readings from all bulk species link concentration sensors are plotted.
|
|
2873
|
+
|
|
2874
|
+
The default is None.
|
|
2875
|
+
show : `bool`, optional
|
|
2876
|
+
If True, the plot/figure is shown in a window.
|
|
2877
|
+
|
|
2878
|
+
Only considered when 'ax' is None.
|
|
2879
|
+
|
|
2880
|
+
The default is True.
|
|
2881
|
+
save_to_file : `str`, optional
|
|
2882
|
+
File to which the plot is saved.
|
|
2883
|
+
|
|
2884
|
+
If specified, 'show' must be set to False --
|
|
2885
|
+
i.e. a plot can not be shown and saved to a file at the same time!
|
|
2886
|
+
|
|
2887
|
+
The default is None.
|
|
2888
|
+
ax : `matplotlib.axes.Axes`, optional
|
|
2889
|
+
If not None, 'ax' is used for plotting.
|
|
2890
|
+
|
|
2891
|
+
The default is None.
|
|
2892
|
+
|
|
2893
|
+
Returns
|
|
2894
|
+
-------
|
|
2895
|
+
`matplotlib.axes.Axes`
|
|
2896
|
+
Plot.
|
|
2897
|
+
"""
|
|
2898
|
+
data = self.get_data_bulk_species_link_concentration(bulk_species_link_sensors)
|
|
2899
|
+
if bulk_species_link_sensors is None:
|
|
2900
|
+
bulk_species_link_sensors = self.__sensor_config.bulk_species_link_sensors
|
|
2901
|
+
|
|
2902
|
+
area_unit = self.__sensor_config.surface_species_area_unit
|
|
2903
|
+
concentration_unit = None
|
|
2904
|
+
labels = []
|
|
2905
|
+
for species_id in bulk_species_link_sensors:
|
|
2906
|
+
mass_unit = self.__sensor_config.get_bulk_species_mass_unit_id(species_id)
|
|
2907
|
+
if concentration_unit is not None:
|
|
2908
|
+
if concentration_unit != mass_unit:
|
|
2909
|
+
raise ValueError("Can not plot species with different mass units")
|
|
2910
|
+
concentration_unit = mass_unit
|
|
2911
|
+
else:
|
|
2912
|
+
concentration_unit = mass_unit
|
|
2913
|
+
|
|
2914
|
+
for link_id in bulk_species_link_sensors[species_id]:
|
|
2915
|
+
labels.append(f"{species_id} @ link {link_id}")
|
|
2916
|
+
|
|
2917
|
+
y_axis_label = f"Concentration in ${massunit_to_str(concentration_unit)}/" +\
|
|
2918
|
+
f"{areaunit_to_str(area_unit)}$"
|
|
2919
|
+
|
|
2920
|
+
return plot_timeseries_data(data.T, labels=labels,
|
|
2921
|
+
x_axis_label=self.__get_x_axis_label(),
|
|
2922
|
+
y_axis_label=y_axis_label,
|
|
2923
|
+
show=show, save_to_file=save_to_file, ax=ax)
|