epyt-flow 0.8.0__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.
Files changed (46) hide show
  1. epyt_flow/VERSION +1 -1
  2. epyt_flow/__init__.py +1 -0
  3. epyt_flow/data/benchmarks/batadal.py +1 -1
  4. epyt_flow/data/benchmarks/battledim.py +4 -3
  5. epyt_flow/data/benchmarks/gecco_water_quality.py +4 -4
  6. epyt_flow/data/benchmarks/leakdb.py +9 -9
  7. epyt_flow/data/benchmarks/water_usage.py +2 -2
  8. epyt_flow/data/networks.py +15 -27
  9. epyt_flow/gym/control_gyms.py +2 -2
  10. epyt_flow/gym/scenario_control_env.py +1 -1
  11. epyt_flow/metrics.py +28 -28
  12. epyt_flow/models/sensor_interpolation_detector.py +3 -3
  13. epyt_flow/rest_api/base_handler.py +4 -4
  14. epyt_flow/rest_api/scada_data/data_handlers.py +11 -11
  15. epyt_flow/rest_api/scada_data/export_handlers.py +2 -2
  16. epyt_flow/rest_api/scada_data/handlers.py +9 -9
  17. epyt_flow/rest_api/scenario/event_handlers.py +6 -6
  18. epyt_flow/rest_api/scenario/handlers.py +15 -15
  19. epyt_flow/rest_api/scenario/simulation_handlers.py +7 -7
  20. epyt_flow/rest_api/scenario/uncertainty_handlers.py +6 -6
  21. epyt_flow/serialization.py +4 -2
  22. epyt_flow/simulation/events/actuator_events.py +1 -1
  23. epyt_flow/simulation/events/leakages.py +1 -1
  24. epyt_flow/simulation/events/quality_events.py +16 -5
  25. epyt_flow/simulation/events/sensor_reading_attack.py +1 -1
  26. epyt_flow/simulation/events/sensor_reading_event.py +3 -3
  27. epyt_flow/simulation/events/system_event.py +1 -1
  28. epyt_flow/simulation/parallel_simulation.py +1 -1
  29. epyt_flow/simulation/scada/advanced_control.py +2 -2
  30. epyt_flow/simulation/scada/scada_data.py +117 -131
  31. epyt_flow/simulation/scada/scada_data_export.py +1 -1
  32. epyt_flow/simulation/scenario_config.py +1 -1
  33. epyt_flow/simulation/scenario_simulator.py +120 -26
  34. epyt_flow/simulation/scenario_visualizer.py +9 -9
  35. epyt_flow/simulation/sensor_config.py +22 -28
  36. epyt_flow/topology.py +2 -2
  37. epyt_flow/uncertainty/model_uncertainty.py +624 -147
  38. epyt_flow/uncertainty/sensor_noise.py +94 -19
  39. epyt_flow/uncertainty/uncertainties.py +4 -4
  40. epyt_flow/uncertainty/utils.py +7 -7
  41. epyt_flow/utils.py +9 -8
  42. {epyt_flow-0.8.0.dist-info → epyt_flow-0.9.0.dist-info}/METADATA +1 -1
  43. {epyt_flow-0.8.0.dist-info → epyt_flow-0.9.0.dist-info}/RECORD +46 -46
  44. {epyt_flow-0.8.0.dist-info → epyt_flow-0.9.0.dist-info}/LICENSE +0 -0
  45. {epyt_flow-0.8.0.dist-info → epyt_flow-0.9.0.dist-info}/WHEEL +0 -0
  46. {epyt_flow-0.8.0.dist-info → epyt_flow-0.9.0.dist-info}/top_level.txt +0 -0
@@ -34,75 +34,75 @@ class ScadaData(Serializable):
34
34
  ----------
35
35
  sensor_config : :class:`~epyt_flow.simulation.sensor_config.SensorConfig`
36
36
  Specifications of all sensors.
37
- sensor_readings_time : `numpy.ndarray`
37
+ sensor_readings_time : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
38
38
  Time (seconds since simulation start) for each sensor reading row
39
39
  in `sensor_readings_data_raw`.
40
40
 
41
41
  This parameter is expected to be a 1d array with the same size as
42
42
  the number of rows in `sensor_readings_data_raw`.
43
- pressure_data_raw : `numpy.ndarray`, optional
43
+ pressure_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
44
44
  Raw pressure values of all nodes as a two-dimensional array --
45
45
  first dimension encodes time, second dimension pressure at nodes.
46
46
 
47
47
  The default is None,
48
- flow_data_raw : `numpy.ndarray`, optional
48
+ flow_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
49
49
  Raw flow values of all links/pipes --
50
50
  first dimension encodes time, second dimension pressure at links/pipes.
51
51
 
52
52
  The default is None.
53
- demand_data_raw : `numpy.ndarray`, optional
53
+ demand_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
54
54
  Raw demand values of all nodes --
55
55
  first dimension encodes time, second dimension demand at nodes.
56
56
 
57
57
  The default is None.
58
- node_quality_data_raw : `numpy.ndarray`, optional
58
+ node_quality_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
59
59
  Raw quality values of all nodes --
60
60
  first dimension encodes time, second dimension quality at nodes.
61
61
 
62
62
  The default is None.
63
- link_quality_data_raw : `numpy.ndarray`, optional
63
+ link_quality_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
64
64
  Raw quality values of all links/pipes --
65
65
  first dimension encodes time, second dimension quality at links/pipes.
66
66
 
67
67
  The default is None.
68
- pumps_state_data_raw : `numpy.ndarray`, optional
68
+ pumps_state_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
69
69
  States of all pumps --
70
70
  first dimension encodes time, second dimension states of pumps.
71
71
 
72
72
  The default is None.
73
- valves_state_data_raw : `numpy.ndarray`, optional
73
+ valves_state_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
74
74
  States of all valves --
75
75
  first dimension encodes time, second dimension states of valves.
76
76
 
77
77
  The default is None.
78
- tanks_volume_data_raw : `numpy.ndarray`, optional
78
+ tanks_volume_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
79
79
  Water volumes in all tanks --
80
80
  first dimension encodes time, second dimension water volume in tanks.
81
81
 
82
82
  The default is None.
83
- surface_species_concentration_raw : `numpy.ndarray`, optional
83
+ surface_species_concentration_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
84
84
  Raw concentrations of surface species as a tree dimensional array --
85
85
  first dimension encodes time, second dimension denotes the different surface species,
86
86
  third dimension denotes species concentrations at links/pipes.
87
87
 
88
88
  The default is None.
89
- bulk_species_node_concentration_raw : `numpy.ndarray`, optional
89
+ bulk_species_node_concentration_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
90
90
  Raw concentrations of bulk species at nodes as a tree dimensional array --
91
91
  first dimension encodes time, second dimension denotes the different bulk species,
92
92
  third dimension denotes species concentrations at nodes.
93
93
 
94
94
  The default is None.
95
- bulk_species_link_concentration_raw : `numpy.ndarray`, optional
95
+ bulk_species_link_concentration_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
96
96
  Raw concentrations of bulk species at links as a tree dimensional array --
97
97
  first dimension encodes time, second dimension denotes the different bulk species,
98
98
  third dimension denotes species concentrations at nodes.
99
99
 
100
100
  The default is None.
101
- pumps_energy_usage_data_raw : `numpy.ndarray`, optional
101
+ pumps_energy_usage_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
102
102
  Energy usage data of each pump.
103
103
 
104
104
  The default is None.
105
- pumps_efficiency_data_raw : `numpy.ndarray`, optional
105
+ pumps_efficiency_data_raw : `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_, optional
106
106
  Pump efficiency data of each pump.
107
107
 
108
108
  The default is None.
@@ -142,8 +142,6 @@ class ScadaData(Serializable):
142
142
  dict[int, bsr_array]] = None,
143
143
  bulk_species_link_concentration_raw: Union[np.ndarray,
144
144
  dict[int, bsr_array]] = None,
145
- pump_energy_usage_data = None,
146
- pump_efficiency_data = None,
147
145
  pumps_energy_usage_data_raw: np.ndarray = None,
148
146
  pumps_efficiency_data_raw: np.ndarray = None,
149
147
  sensor_faults: list[SensorFault] = [],
@@ -289,11 +287,6 @@ class ScadaData(Serializable):
289
287
  raise TypeError("'frozen_sensor_config' must be an instance of 'bool' " +
290
288
  f"but not of '{type(frozen_sensor_config)}'")
291
289
 
292
- if pump_efficiency_data is not None or pump_energy_usage_data is not None:
293
- warnings.warn("Loading a file that was created with an outdated version of EPyT-Flow" +
294
- " -- support of such old files will be removed in the next release!",
295
- DeprecationWarning)
296
-
297
290
  def __raise_shape_mismatch(var_name: str) -> None:
298
291
  raise ValueError(f"Shape mismatch in '{var_name}' -- " +
299
292
  "i.e number of time steps in 'sensor_readings_time' " +
@@ -1160,7 +1153,7 @@ class ScadaData(Serializable):
1160
1153
 
1161
1154
  Returns
1162
1155
  -------
1163
- `numpy.ndarray`
1156
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1164
1157
  Raw pressure readings.
1165
1158
  """
1166
1159
  return deepcopy(self.__pressure_data_raw)
@@ -1172,7 +1165,7 @@ class ScadaData(Serializable):
1172
1165
 
1173
1166
  Returns
1174
1167
  -------
1175
- `numpy.ndarray`
1168
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1176
1169
  Raw flow readings.
1177
1170
  """
1178
1171
  return deepcopy(self.__flow_data_raw)
@@ -1184,7 +1177,7 @@ class ScadaData(Serializable):
1184
1177
 
1185
1178
  Returns
1186
1179
  -------
1187
- `numpy.ndarray`
1180
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1188
1181
  Raw demand readings.
1189
1182
  """
1190
1183
  return deepcopy(self.__demand_data_raw)
@@ -1196,7 +1189,7 @@ class ScadaData(Serializable):
1196
1189
 
1197
1190
  Returns
1198
1191
  -------
1199
- `numpy.ndarray`
1192
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1200
1193
  Raw node quality readings.
1201
1194
  """
1202
1195
  return deepcopy(self.__node_quality_data_raw)
@@ -1208,7 +1201,7 @@ class ScadaData(Serializable):
1208
1201
 
1209
1202
  Returns
1210
1203
  -------
1211
- `numpy.ndarray`
1204
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1212
1205
  Raw link quality readings.
1213
1206
  """
1214
1207
  return deepcopy(self.__link_quality_data_raw)
@@ -1220,7 +1213,7 @@ class ScadaData(Serializable):
1220
1213
 
1221
1214
  Returns
1222
1215
  -------
1223
- `numpy.ndarray`
1216
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1224
1217
  Sensor readings time stamps.
1225
1218
  """
1226
1219
  return deepcopy(self.__sensor_readings_time)
@@ -1232,7 +1225,7 @@ class ScadaData(Serializable):
1232
1225
 
1233
1226
  Returns
1234
1227
  -------
1235
- `numpy.ndarray`
1228
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1236
1229
  Raw pump state readings.
1237
1230
  """
1238
1231
  return deepcopy(self.__pumps_state_data_raw)
@@ -1244,7 +1237,7 @@ class ScadaData(Serializable):
1244
1237
 
1245
1238
  Returns
1246
1239
  -------
1247
- `numpy.ndarray`
1240
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1248
1241
  Raw valve state readings.
1249
1242
  """
1250
1243
  return deepcopy(self.__valves_state_data_raw)
@@ -1256,7 +1249,7 @@ class ScadaData(Serializable):
1256
1249
 
1257
1250
  Returns
1258
1251
  -------
1259
- `numpy.ndarray`
1252
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1260
1253
  Raw tank volume readings.
1261
1254
  """
1262
1255
  return deepcopy(self.__tanks_volume_data_raw)
@@ -1268,7 +1261,7 @@ class ScadaData(Serializable):
1268
1261
 
1269
1262
  Returns
1270
1263
  -------
1271
- `numpy.ndarray`
1264
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1272
1265
  Raw species concentrations.
1273
1266
  """
1274
1267
  return deepcopy(self.__surface_species_concentration_raw)
@@ -1280,7 +1273,7 @@ class ScadaData(Serializable):
1280
1273
 
1281
1274
  Returns
1282
1275
  -------
1283
- `numpy.ndarray`
1276
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1284
1277
  Raw species concentrations.
1285
1278
  """
1286
1279
  return deepcopy(self.__bulk_species_node_concentration_raw)
@@ -1292,7 +1285,7 @@ class ScadaData(Serializable):
1292
1285
 
1293
1286
  Returns
1294
1287
  -------
1295
- `numpy.ndarray`
1288
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1296
1289
  Raw species concentrations.
1297
1290
  """
1298
1291
  return deepcopy(self.__bulk_species_link_concentration_raw)
@@ -1304,7 +1297,7 @@ class ScadaData(Serializable):
1304
1297
 
1305
1298
  Returns
1306
1299
  -------
1307
- `numpy.ndarray`
1300
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1308
1301
  Energy consumption of each pump.
1309
1302
  """
1310
1303
  return deepcopy(self.__pumps_energy_usage_data_raw)
@@ -1316,59 +1309,49 @@ class ScadaData(Serializable):
1316
1309
 
1317
1310
  Returns
1318
1311
  -------
1319
- `numpy.ndarray`
1312
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1320
1313
  Pumps' efficiency.
1321
1314
  """
1322
1315
  return deepcopy(self.__pumps_efficiency_data_raw)
1323
1316
 
1317
+ def __map_sensor_to_idx(self, sensor_type: int, sensor_id: str) -> int:
1318
+ if sensor_type == SENSOR_TYPE_NODE_PRESSURE:
1319
+ return self.__sensor_config.get_index_of_reading(pressure_sensor=sensor_id)
1320
+ elif sensor_type == SENSOR_TYPE_NODE_QUALITY:
1321
+ return self.__sensor_config.get_index_of_reading(node_quality_sensor=sensor_id)
1322
+ elif sensor_type == SENSOR_TYPE_NODE_DEMAND:
1323
+ return self.__sensor_config.get_index_of_reading(demand_sensor=sensor_id)
1324
+ elif sensor_type == SENSOR_TYPE_LINK_FLOW:
1325
+ return self.__sensor_config.get_index_of_reading(flow_sensor=sensor_id)
1326
+ elif sensor_type == SENSOR_TYPE_LINK_QUALITY:
1327
+ return self.__sensor_config.get_index_of_reading(link_quality_sensor=sensor_id)
1328
+ elif sensor_type == SENSOR_TYPE_VALVE_STATE:
1329
+ return self.__sensor_config.get_index_of_reading(valve_state_sensor=sensor_id)
1330
+ elif sensor_type == SENSOR_TYPE_PUMP_STATE:
1331
+ return self.__sensor_config.get_index_of_reading(pump_state_sensor=sensor_id)
1332
+ elif sensor_type == SENSOR_TYPE_PUMP_EFFICIENCY:
1333
+ return self.__sensor_config.get_index_of_reading(pump_efficiency_sensor=sensor_id)
1334
+ elif sensor_type == SENSOR_TYPE_PUMP_ENERGYCONSUMPTION:
1335
+ return self.__sensor_config.get_index_of_reading(pump_energyconsumption_sensor=sensor_id)
1336
+ elif sensor_type == SENSOR_TYPE_TANK_VOLUME:
1337
+ return self.__sensor_config.get_index_of_reading(tank_volume_sensor=sensor_id)
1338
+ elif sensor_type == SENSOR_TYPE_NODE_BULK_SPECIES:
1339
+ return self.__sensor_config.get_index_of_reading(bulk_species_node_sensor=sensor_id)
1340
+ elif sensor_type == SENSOR_TYPE_LINK_BULK_SPECIES:
1341
+ return self.__sensor_config.get_index_of_reading(bulk_species_link_sensor=sensor_id)
1342
+ elif sensor_type == SENSOR_TYPE_SURFACE_SPECIES:
1343
+ return self.__sensor_config.get_index_of_reading(surface_species_sensor=sensor_id)
1344
+ else:
1345
+ raise ValueError(f"Unknown sensor type '{sensor_type}'")
1346
+
1324
1347
  def __init(self):
1325
- self.__apply_sensor_noise = lambda x: x
1348
+ self.__apply_global_sensor_noise = lambda x: x
1326
1349
  if self.__sensor_noise is not None:
1327
- self.__apply_sensor_noise = self.__sensor_noise.apply
1350
+ self.__apply_global_sensor_noise = self.__sensor_noise.apply_global_uncertainty
1328
1351
 
1329
1352
  self.__apply_sensor_reading_events = []
1330
1353
  for sensor_event in self.__sensor_reading_events:
1331
- idx = None
1332
- if sensor_event.sensor_type == SENSOR_TYPE_NODE_PRESSURE:
1333
- idx = self.__sensor_config.get_index_of_reading(
1334
- pressure_sensor=sensor_event.sensor_id)
1335
- elif sensor_event.sensor_type == SENSOR_TYPE_NODE_QUALITY:
1336
- idx = self.__sensor_config.get_index_of_reading(
1337
- node_quality_sensor=sensor_event.sensor_id)
1338
- elif sensor_event.sensor_type == SENSOR_TYPE_NODE_DEMAND:
1339
- idx = self.__sensor_config.get_index_of_reading(
1340
- demand_sensor=sensor_event.sensor_id)
1341
- elif sensor_event.sensor_type == SENSOR_TYPE_LINK_FLOW:
1342
- idx = self.__sensor_config.get_index_of_reading(
1343
- flow_sensor=sensor_event.sensor_id)
1344
- elif sensor_event.sensor_type == SENSOR_TYPE_LINK_QUALITY:
1345
- idx = self.__sensor_config.get_index_of_reading(
1346
- link_quality_sensor=sensor_event.sensor_id)
1347
- elif sensor_event.sensor_type == SENSOR_TYPE_VALVE_STATE:
1348
- idx = self.__sensor_config.get_index_of_reading(
1349
- valve_state_sensor=sensor_event.sensor_id)
1350
- elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_STATE:
1351
- idx = self.__sensor_config.get_index_of_reading(
1352
- pump_state_sensor=sensor_event.sensor_id)
1353
- elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_EFFICIENCY:
1354
- idx = self.__sensor_config.get_index_of_reading(
1355
- pump_efficiency_sensor=sensor_event.sensor_id)
1356
- elif sensor_event.sensor_type == SENSOR_TYPE_PUMP_ENERGYCONSUMPTION:
1357
- idx = self.__sensor_config.get_index_of_reading(
1358
- pump_energyconsumption_sensor=sensor_event.sensor_id)
1359
- elif sensor_event.sensor_type == SENSOR_TYPE_TANK_VOLUME:
1360
- idx = self.__sensor_config.get_index_of_reading(
1361
- tank_volume_sensor=sensor_event.sensor_id)
1362
- elif sensor_event.sensor_type == SENSOR_TYPE_NODE_BULK_SPECIES:
1363
- idx = self.__sensor_config.get_index_of_reading(
1364
- bulk_species_node_sensor=sensor_event.sensor_id)
1365
- elif sensor_event.sensor_type == SENSOR_TYPE_LINK_BULK_SPECIES:
1366
- idx = self.__sensor_config.get_index_of_reading(
1367
- bulk_species_link_sensor=sensor_event.sensor_id)
1368
- elif sensor_event.sensor_type == SENSOR_TYPE_SURFACE_SPECIES:
1369
- idx = self.__sensor_config.get_index_of_reading(
1370
- surface_species_sensor=sensor_event.sensor_id)
1371
-
1354
+ idx = self.__map_sensor_to_idx(sensor_event.sensor_type, sensor_event.sensor_id)
1372
1355
  self.__apply_sensor_reading_events.append((idx, sensor_event.apply))
1373
1356
 
1374
1357
  self.__sensor_readings = None
@@ -1959,7 +1942,7 @@ class ScadaData(Serializable):
1959
1942
 
1960
1943
  if self.__pumps_energy_usage_data_raw is not None:
1961
1944
  self.__pumps_energy_usage_data_raw = np.concatenate(
1962
- (self.__pumps_energy_usage_data_raw, other.pumps_energy_usage_data_raw),
1945
+ (self.__pumps_energy_usage_data_raw, other.pumps_energyconsumption_data_raw),
1963
1946
  axis=0)
1964
1947
 
1965
1948
  if self.__pumps_efficiency_data_raw is not None:
@@ -1974,7 +1957,7 @@ class ScadaData(Serializable):
1974
1957
 
1975
1958
  Returns
1976
1959
  -------
1977
- `numpy.ndarray`
1960
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
1978
1961
  Final sensor readings.
1979
1962
  """
1980
1963
  # Comute clean sensor readings
@@ -2026,18 +2009,21 @@ class ScadaData(Serializable):
2026
2009
  sensor_readings = np.concatenate(data, axis=1)
2027
2010
 
2028
2011
  # Apply sensor uncertainties
2029
- state_sensors_idx = [] # Pump states and valve states are NOT affected!
2030
- for link_id in self.sensor_config.pump_state_sensors:
2031
- state_sensors_idx.append(
2032
- self.__sensor_config.get_index_of_reading(pump_state_sensor=link_id))
2033
- for link_id in self.sensor_config.valve_state_sensors:
2034
- state_sensors_idx.append(
2035
- self.__sensor_config.get_index_of_reading(valve_state_sensor=link_id))
2012
+ if self.__sensor_noise is not None:
2013
+ state_sensors_idx = [] # Pump states and valve states are NOT affected!
2014
+ for link_id in self.sensor_config.pump_state_sensors:
2015
+ state_sensors_idx.append(
2016
+ self.__sensor_config.get_index_of_reading(pump_state_sensor=link_id))
2017
+ for link_id in self.sensor_config.valve_state_sensors:
2018
+ state_sensors_idx.append(
2019
+ self.__sensor_config.get_index_of_reading(valve_state_sensor=link_id))
2036
2020
 
2037
- mask = np.ones(sensor_readings.shape[1], dtype=bool)
2038
- mask[state_sensors_idx] = False
2021
+ mask = np.ones(sensor_readings.shape[1], dtype=bool)
2022
+ mask[state_sensors_idx] = False
2023
+ sensor_readings[:, mask] = self.__apply_global_sensor_noise(sensor_readings[:, mask])
2039
2024
 
2040
- sensor_readings[:, mask] = self.__apply_sensor_noise(sensor_readings[:, mask])
2025
+ sensor_readings = self.__sensor_noise.apply_local_uncertainty(self.__map_sensor_to_idx,
2026
+ sensor_readings)
2041
2027
 
2042
2028
  # Apply sensor faults
2043
2029
  for idx, f in self.__apply_sensor_reading_events:
@@ -2075,7 +2061,7 @@ class ScadaData(Serializable):
2075
2061
 
2076
2062
  Returns
2077
2063
  -------
2078
- `numpy.ndarray`
2064
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2079
2065
  Pressure sensor readings.
2080
2066
  """
2081
2067
  if self.__sensor_config.pressure_sensors == []:
@@ -2125,14 +2111,14 @@ class ScadaData(Serializable):
2125
2111
  i.e. a plot can not be shown and saved to a file at the same time!
2126
2112
 
2127
2113
  The default is None.
2128
- ax : `matplotlib.axes.Axes`, optional
2114
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2129
2115
  If not None, 'ax' is used for plotting.
2130
2116
 
2131
2117
  The default is None.
2132
2118
 
2133
2119
  Returns
2134
2120
  -------
2135
- `matplotlib.axes.Axes`
2121
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2136
2122
  Plot.
2137
2123
  """
2138
2124
  data = self.get_data_pressures(sensor_locations)
@@ -2162,7 +2148,7 @@ class ScadaData(Serializable):
2162
2148
 
2163
2149
  Returns
2164
2150
  -------
2165
- `numpy.ndarray`
2151
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2166
2152
  Flow sensor readings.
2167
2153
  """
2168
2154
  if self.__sensor_config.flow_sensors == []:
@@ -2212,14 +2198,14 @@ class ScadaData(Serializable):
2212
2198
  i.e. a plot can not be shown and saved to a file at the same time!
2213
2199
 
2214
2200
  The default is None.
2215
- ax : `matplotlib.axes.Axes`, optional
2201
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2216
2202
  If not None, 'ax' is used for plotting.
2217
2203
 
2218
2204
  The default is None.
2219
2205
 
2220
2206
  Returns
2221
2207
  -------
2222
- `matplotlib.axes.Axes`
2208
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2223
2209
  Plot.
2224
2210
  """
2225
2211
  data = self.get_data_flows(sensor_locations)
@@ -2248,7 +2234,7 @@ class ScadaData(Serializable):
2248
2234
 
2249
2235
  Returns
2250
2236
  -------
2251
- `numpy.ndarray`
2237
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2252
2238
  Demand sensor readings.
2253
2239
  """
2254
2240
  if self.__sensor_config.demand_sensors == []:
@@ -2298,14 +2284,14 @@ class ScadaData(Serializable):
2298
2284
  i.e. a plot can not be shown and saved to a file at the same time!
2299
2285
 
2300
2286
  The default is None.
2301
- ax : `matplotlib.axes.Axes`, optional
2287
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2302
2288
  If not None, 'ax' is used for plotting.
2303
2289
 
2304
2290
  The default is None.
2305
2291
 
2306
2292
  Returns
2307
2293
  -------
2308
- `matplotlib.axes.Axes`
2294
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2309
2295
  Plot.
2310
2296
  """
2311
2297
  data = self.get_data_demands(sensor_locations)
@@ -2334,7 +2320,7 @@ class ScadaData(Serializable):
2334
2320
 
2335
2321
  Returns
2336
2322
  -------
2337
- `numpy.ndarray`
2323
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2338
2324
  Node quality sensor readings.
2339
2325
  """
2340
2326
  if self.__sensor_config.quality_node_sensors == []:
@@ -2386,14 +2372,14 @@ class ScadaData(Serializable):
2386
2372
  i.e. a plot can not be shown and saved to a file at the same time!
2387
2373
 
2388
2374
  The default is None.
2389
- ax : `matplotlib.axes.Axes`, optional
2375
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2390
2376
  If not None, 'ax' is used for plotting.
2391
2377
 
2392
2378
  The default is None.
2393
2379
 
2394
2380
  Returns
2395
2381
  -------
2396
- `matplotlib.axes.Axes`
2382
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2397
2383
  Plot.
2398
2384
  """
2399
2385
  data = self.get_data_nodes_quality(sensor_locations)
@@ -2423,7 +2409,7 @@ class ScadaData(Serializable):
2423
2409
 
2424
2410
  Returns
2425
2411
  -------
2426
- `numpy.ndarray`
2412
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2427
2413
  Link quality sensor readings.
2428
2414
  """
2429
2415
  if self.__sensor_config.quality_link_sensors == []:
@@ -2475,14 +2461,14 @@ class ScadaData(Serializable):
2475
2461
  i.e. a plot can not be shown and saved to a file at the same time!
2476
2462
 
2477
2463
  The default is None.
2478
- ax : `matplotlib.axes.Axes`, optional
2464
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2479
2465
  If not None, 'ax' is used for plotting.
2480
2466
 
2481
2467
  The default is None.
2482
2468
 
2483
2469
  Returns
2484
2470
  -------
2485
- `matplotlib.axes.Axes`
2471
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2486
2472
  Plot.
2487
2473
  """
2488
2474
  data = self.get_data_links_quality(sensor_locations)
@@ -2512,7 +2498,7 @@ class ScadaData(Serializable):
2512
2498
 
2513
2499
  Returns
2514
2500
  -------
2515
- `numpy.ndarray`
2501
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2516
2502
  Pump state sensor readings.
2517
2503
  """
2518
2504
  if self.__sensor_config.pump_state_sensors == []:
@@ -2563,14 +2549,14 @@ class ScadaData(Serializable):
2563
2549
  i.e. a plot can not be shown and saved to a file at the same time!
2564
2550
 
2565
2551
  The default is None.
2566
- ax : `matplotlib.axes.Axes`, optional
2552
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2567
2553
  If not None, 'ax' is used for plotting.
2568
2554
 
2569
2555
  The default is None.
2570
2556
 
2571
2557
  Returns
2572
2558
  -------
2573
- `matplotlib.axes.Axes`
2559
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2574
2560
  Plot.
2575
2561
  """
2576
2562
  data = self.get_data_pumps_state(sensor_locations)
@@ -2599,7 +2585,7 @@ class ScadaData(Serializable):
2599
2585
 
2600
2586
  Returns
2601
2587
  -------
2602
- `numpy.ndarray`
2588
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2603
2589
  Pump efficiency sensor readings.
2604
2590
  """
2605
2591
  if self.__sensor_config.pump_efficiency_sensors == []:
@@ -2651,14 +2637,14 @@ class ScadaData(Serializable):
2651
2637
  i.e. a plot can not be shown and saved to a file at the same time!
2652
2638
 
2653
2639
  The default is None.
2654
- ax : `matplotlib.axes.Axes`, optional
2640
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2655
2641
  If not None, 'ax' is used for plotting.
2656
2642
 
2657
2643
  The default is None.
2658
2644
 
2659
2645
  Returns
2660
2646
  -------
2661
- `matplotlib.axes.Axes`
2647
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2662
2648
  Plot.
2663
2649
  """
2664
2650
  data = self.get_data_pumps_efficiency(sensor_locations)
@@ -2668,7 +2654,7 @@ class ScadaData(Serializable):
2668
2654
  return plot_timeseries_data(data.T, labels=[f"Pump {n_id}"
2669
2655
  for n_id in pump_efficiency_sensors],
2670
2656
  x_axis_label=self.__get_x_axis_label(),
2671
- y_axis_label="Efficiency in $\%$",
2657
+ y_axis_label="Efficiency in $%$",
2672
2658
  show=show, save_to_file=save_to_file, ax=ax)
2673
2659
 
2674
2660
  def get_data_pumps_energyconsumption(self, sensor_locations: list[str] = None) -> np.ndarray:
@@ -2687,7 +2673,7 @@ class ScadaData(Serializable):
2687
2673
 
2688
2674
  Returns
2689
2675
  -------
2690
- `numpy.ndarray`
2676
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2691
2677
  Pump energy consumption sensor readings.
2692
2678
  """
2693
2679
  if self.__sensor_config.pump_energyconsumption_sensors == []:
@@ -2739,14 +2725,14 @@ class ScadaData(Serializable):
2739
2725
  i.e. a plot can not be shown and saved to a file at the same time!
2740
2726
 
2741
2727
  The default is None.
2742
- ax : `matplotlib.axes.Axes`, optional
2728
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2743
2729
  If not None, 'ax' is used for plotting.
2744
2730
 
2745
2731
  The default is None.
2746
2732
 
2747
2733
  Returns
2748
2734
  -------
2749
- `matplotlib.axes.Axes`
2735
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2750
2736
  Plot.
2751
2737
  """
2752
2738
  data = self.get_data_pumps_energyconsumption(sensor_locations)
@@ -2774,7 +2760,7 @@ class ScadaData(Serializable):
2774
2760
 
2775
2761
  Returns
2776
2762
  -------
2777
- `numpy.ndarray`
2763
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2778
2764
  Valve state sensor readings.
2779
2765
  """
2780
2766
  if self.__sensor_config.valve_state_sensors == []:
@@ -2825,14 +2811,14 @@ class ScadaData(Serializable):
2825
2811
  i.e. a plot can not be shown and saved to a file at the same time!
2826
2812
 
2827
2813
  The default is None.
2828
- ax : `matplotlib.axes.Axes`, optional
2814
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2829
2815
  If not None, 'ax' is used for plotting.
2830
2816
 
2831
2817
  The default is None.
2832
2818
 
2833
2819
  Returns
2834
2820
  -------
2835
- `matplotlib.axes.Axes`
2821
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2836
2822
  Plot.
2837
2823
  """
2838
2824
  data = self.get_data_valves_state(sensor_locations)
@@ -2861,7 +2847,7 @@ class ScadaData(Serializable):
2861
2847
 
2862
2848
  Returns
2863
2849
  -------
2864
- `numpy.ndarray`
2850
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2865
2851
  Water tanks volume sensor readings.
2866
2852
  """
2867
2853
  if self.__sensor_config.tank_volume_sensors == []:
@@ -2912,14 +2898,14 @@ class ScadaData(Serializable):
2912
2898
  i.e. a plot can not be shown and saved to a file at the same time!
2913
2899
 
2914
2900
  The default is None.
2915
- ax : `matplotlib.axes.Axes`, optional
2901
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
2916
2902
  If not None, 'ax' is used for plotting.
2917
2903
 
2918
2904
  The default is None.
2919
2905
 
2920
2906
  Returns
2921
2907
  -------
2922
- `matplotlib.axes.Axes`
2908
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
2923
2909
  Plot.
2924
2910
  """
2925
2911
  data = self.get_data_tanks_water_volume(sensor_locations)
@@ -2953,7 +2939,7 @@ class ScadaData(Serializable):
2953
2939
 
2954
2940
  Returns
2955
2941
  -------
2956
- `numpy.ndarray`
2942
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
2957
2943
  Surface species concentration sensor readings.
2958
2944
  """
2959
2945
  if self.__sensor_config.surface_species_sensors == {}:
@@ -3014,14 +3000,14 @@ class ScadaData(Serializable):
3014
3000
  i.e. a plot can not be shown and saved to a file at the same time!
3015
3001
 
3016
3002
  The default is None.
3017
- ax : `matplotlib.axes.Axes`, optional
3003
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
3018
3004
  If not None, 'ax' is used for plotting.
3019
3005
 
3020
3006
  The default is None.
3021
3007
 
3022
3008
  Returns
3023
3009
  -------
3024
- `matplotlib.axes.Axes`
3010
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
3025
3011
  Plot.
3026
3012
  """
3027
3013
  data = self.get_data_surface_species_concentration(surface_species_sensor_locations)
@@ -3069,7 +3055,7 @@ class ScadaData(Serializable):
3069
3055
 
3070
3056
  Returns
3071
3057
  -------
3072
- `numpy.ndarray`
3058
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
3073
3059
  Bulk species concentration sensor readings.
3074
3060
  """
3075
3061
  if self.__sensor_config.bulk_species_node_sensors == {}:
@@ -3130,14 +3116,14 @@ class ScadaData(Serializable):
3130
3116
  i.e. a plot can not be shown and saved to a file at the same time!
3131
3117
 
3132
3118
  The default is None.
3133
- ax : `matplotlib.axes.Axes`, optional
3119
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
3134
3120
  If not None, 'ax' is used for plotting.
3135
3121
 
3136
3122
  The default is None.
3137
3123
 
3138
3124
  Returns
3139
3125
  -------
3140
- `matplotlib.axes.Axes`
3126
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
3141
3127
  Plot.
3142
3128
  """
3143
3129
  data = self.get_data_bulk_species_node_concentration(bulk_species_node_sensors)
@@ -3184,7 +3170,7 @@ class ScadaData(Serializable):
3184
3170
 
3185
3171
  Returns
3186
3172
  -------
3187
- `numpy.ndarray`
3173
+ `numpy.ndarray <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html>`_
3188
3174
  Bulk species concentration sensor readings.
3189
3175
  """
3190
3176
  if self.__sensor_config.bulk_species_link_sensors == {}:
@@ -3245,14 +3231,14 @@ class ScadaData(Serializable):
3245
3231
  i.e. a plot can not be shown and saved to a file at the same time!
3246
3232
 
3247
3233
  The default is None.
3248
- ax : `matplotlib.axes.Axes`, optional
3234
+ ax : `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_, optional
3249
3235
  If not None, 'ax' is used for plotting.
3250
3236
 
3251
3237
  The default is None.
3252
3238
 
3253
3239
  Returns
3254
3240
  -------
3255
- `matplotlib.axes.Axes`
3241
+ `matplotlib.axes.Axes <https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.html>`_
3256
3242
  Plot.
3257
3243
  """
3258
3244
  data = self.get_data_bulk_species_link_concentration(bulk_species_link_sensors)
@@ -3296,7 +3282,7 @@ class ScadaData(Serializable):
3296
3282
 
3297
3283
  Returns
3298
3284
  -------
3299
- `pandas.DataFrame`
3285
+ `pandas.DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html>`_
3300
3286
  Exported data.
3301
3287
  """
3302
3288
  from .scada_data_export import ScadaDataExport