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.
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 +7 -7
  7. epyt_flow/data/benchmarks/water_usage.py +2 -2
  8. epyt_flow/data/networks.py +1 -1
  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.1.dist-info → epyt_flow-0.9.0.dist-info}/METADATA +1 -1
  43. {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/RECORD +46 -46
  44. {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/LICENSE +0 -0
  45. {epyt_flow-0.8.1.dist-info → epyt_flow-0.9.0.dist-info}/WHEEL +0 -0
  46. {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) -> None:
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 isinstance(demand_pattern, np.ndarray):
968
- raise TypeError("'demand_pattern' must be an instance of 'numpy.ndarray' " +
969
- f"but not of '{type(demand_pattern)}'")
970
- if len(demand_pattern.shape) > 1:
971
- raise ValueError(f"Inconsistent demand pattern shape '{demand_pattern.shape}' " +
972
- "detected. Expected a one dimensional array!")
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
- self.epanet_api.addPattern(demand_pattern_id, demand_pattern)
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, source_type: int,
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
- pattern_idx = self.epanet_api.addPattern(pattern_id, pattern)
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:`epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
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:`epyt_flow.simulation.scenario_simulator.ScenarioSimulator`.
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.scad_data.ScadaData`, optional
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 = None,
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 is not None:
689
- if not isinstance(flow_unit, int):
690
- raise TypeError("'flow_unit' must be a an instance of 'int' " +
691
- f"but not of '{type(flow_unit)}'")
692
- if flow_unit not in range(10):
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` instances.
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]