epyt-flow 0.2.0__py3-none-any.whl → 0.4.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.
@@ -4,6 +4,7 @@ Module provides a class for specifying scenario configurations.
4
4
  from typing import Any
5
5
  from copy import deepcopy
6
6
  import json
7
+ import numpy as np
7
8
 
8
9
  from ..uncertainty import AbsoluteGaussianUncertainty, RelativeGaussianUncertainty, \
9
10
  AbsoluteUniformUncertainty, RelativeUniformUncertainty, ModelUncertainty, \
@@ -350,10 +351,11 @@ class ScenarioConfig(Serializable):
350
351
  return self.__f_inp_in == other.f_inp_in and self.__f_msx_in == other.f_msx_in \
351
352
  and self.__general_params == other.general_params \
352
353
  and self.__memory_consumption_estimate == other.memory_consumption_estimate \
353
- and self.__sensor_config == other.sensor_config and self.__controls == other.controls \
354
+ and self.__sensor_config == other.sensor_config \
355
+ and np.all(self.__controls == other.controls) \
354
356
  and self.__model_uncertainty == other.model_uncertainty \
355
- and self.__system_events == other.system_events \
356
- and self.__sensor_reading_events == other.sensor_reading_events
357
+ and np.all(self.__system_events == other.system_events) \
358
+ and np.all(self.__sensor_reading_events == other.sensor_reading_events)
357
359
 
358
360
  def __str__(self) -> str:
359
361
  return f"f_inp_in: {self.f_inp_in} f_msx_in: {self.f_msx_in} " + \
@@ -415,8 +417,8 @@ class ScenarioConfig(Serializable):
415
417
  general_params["demand_model"] = general_settings["demand_model"]
416
418
  if "quality_model" in general_settings.keys():
417
419
  general_params["quality_model"] = general_settings["quality_model"]
418
- if "flow_units" in general_settings.keys():
419
- general_params["flow_units"] = general_settings["flow_units"]
420
+ if "flow_units_id" in general_settings.keys():
421
+ general_params["flow_units_id"] = general_settings["flow_units_id"]
420
422
 
421
423
  sensor_config = data["sensors"]
422
424
 
@@ -17,8 +17,8 @@ from epyt.epanet import ToolkitConstants
17
17
  from tqdm import tqdm
18
18
 
19
19
  from .scenario_config import ScenarioConfig
20
- from .sensor_config import SensorConfig, areaunit_to_id, massunit_to_id, qualityunits_to_id, \
21
- qualityunits_to_str, MASS_UNIT_MG, \
20
+ from .sensor_config import SensorConfig, areaunit_to_id, massunit_to_id, qualityunit_to_id, \
21
+ qualityunit_to_str, MASS_UNIT_MG, \
22
22
  SENSOR_TYPE_LINK_FLOW, SENSOR_TYPE_LINK_QUALITY, SENSOR_TYPE_NODE_DEMAND, \
23
23
  SENSOR_TYPE_NODE_PRESSURE, SENSOR_TYPE_NODE_QUALITY, \
24
24
  SENSOR_TYPE_PUMP_STATE, SENSOR_TYPE_TANK_VOLUME, SENSOR_TYPE_VALVE_STATE, \
@@ -137,7 +137,7 @@ class ScenarioSimulator():
137
137
  tank_id_to_idx: dict = None, bulkspecies_id_to_idx: dict = None,
138
138
  surfacespecies_id_to_idx: dict = None) -> SensorConfig:
139
139
  flow_unit = self.epanet_api.api.ENgetflowunits()
140
- quality_unit = qualityunits_to_id(self.epanet_api.getQualityInfo().QualityChemUnits)
140
+ quality_unit = qualityunit_to_id(self.epanet_api.getQualityInfo().QualityChemUnits)
141
141
  bulk_species = []
142
142
  surface_species = []
143
143
  bulk_species_mass_unit = []
@@ -298,6 +298,7 @@ class ScenarioSimulator():
298
298
 
299
299
  return deepcopy(list(filter(lambda e: isinstance(e, Leakage), self.__system_events)))
300
300
 
301
+ @property
301
302
  def actuator_events(self) -> list[ActuatorEvent]:
302
303
  """
303
304
  Gets all actuator events.
@@ -483,6 +484,7 @@ class ScenarioSimulator():
483
484
 
484
485
  if inp_file_path is not None:
485
486
  self.epanet_api.saveInputFile(inp_file_path)
487
+ self.__f_inp_in = inp_file_path
486
488
 
487
489
  if export_sensor_config is True:
488
490
  report_desc = "\n\n[REPORT]\n"
@@ -537,6 +539,7 @@ class ScenarioSimulator():
537
539
 
538
540
  if self.__f_msx_in is not None and msx_file_path is not None:
539
541
  self.epanet_api.saveMSXFile(msx_file_path)
542
+ self.__f_msx_in = msx_file_path
540
543
 
541
544
  if export_sensor_config is True:
542
545
  report_desc = "\n\n[REPORT]\n"
@@ -691,7 +694,7 @@ class ScenarioSimulator():
691
694
  return {"code": qual_info.QualityCode,
692
695
  "type": qual_info.QualityType,
693
696
  "chemical_name": qual_info.QualityChemName,
694
- "units": qualityunits_to_id(qual_info.QualityChemUnits),
697
+ "units": qualityunit_to_id(qual_info.QualityChemUnits),
695
698
  "trace_node_id": qual_info.TraceNode}
696
699
 
697
700
  def get_scenario_config(self) -> ScenarioConfig:
@@ -708,8 +711,9 @@ class ScenarioSimulator():
708
711
 
709
712
  general_params = {"hydraulic_time_step": self.get_hydraulic_time_step(),
710
713
  "quality_time_step": self.get_quality_time_step(),
714
+ "reporting_time_step": self.epanet_api.getTimeReportingStep(),
711
715
  "simulation_duration": self.get_simulation_duration(),
712
- "flow_units": self.get_flow_units(),
716
+ "flow_units_id": self.get_flow_units(),
713
717
  "quality_model": self.get_quality_model(),
714
718
  "demand_model": self.get_demand_model()}
715
719
 
@@ -761,8 +765,12 @@ class ScenarioSimulator():
761
765
  nodes_id = self.epanet_api.getNodeNameID()
762
766
  nodes_elevation = self.epanet_api.getNodeElevations()
763
767
  nodes_type = [self.epanet_api.TYPENODE[i] for i in self.epanet_api.getNodeTypeIndex()]
768
+ nodes_coord = [self.epanet_api.api.ENgetcoord(node_idx)
769
+ for node_idx in self.epanet_api.getNodeIndex()]
770
+ node_tank_names = self.epanet_api.getNodeTankNameID()
764
771
 
765
772
  links_id = self.epanet_api.getLinkNameID()
773
+ links_type = self.epanet_api.getLinkType()
766
774
  links_data = self.epanet_api.getNodesConnectingLinksID()
767
775
  links_diameter = self.epanet_api.getLinkDiameter()
768
776
  links_length = self.epanet_api.getLinkLength()
@@ -771,22 +779,49 @@ class ScenarioSimulator():
771
779
  links_wall_coeff = self.epanet_api.getLinkWallReactionCoeff()
772
780
  links_loss_coeff = self.epanet_api.getLinkMinorLossCoeff()
773
781
 
782
+ pumps_id = self.epanet_api.getLinkPumpNameID()
783
+ pumps_type = self.epanet_api.getLinkPumpType()
784
+
785
+ valves_id = self.epanet_api.getLinkValveNameID()
786
+
774
787
  # Build graph describing the topology
775
788
  nodes = []
776
- for node, node_elevation, node_type in zip(nodes_id, nodes_elevation, nodes_type):
777
- nodes.append((node, {"elevation": node_elevation, "type": node_type}))
789
+ for node_id, node_elevation, node_type, node_coord in zip(nodes_id, nodes_elevation,
790
+ nodes_type, nodes_coord):
791
+ node_info = {"elevation": node_elevation,
792
+ "coord": node_coord,
793
+ "type": node_type}
794
+ if node_type == "TANK":
795
+ node_tank_idx = node_tank_names.index(node_id) + 1
796
+ node_info["diameter"] = float(self.epanet_api.getNodeTankDiameter(node_tank_idx))
778
797
 
779
- links = []
780
- for link_id, link, diameter, length, roughness_coeff, bulk_coeff, wall_coeff, loss_coeff \
781
- in zip(links_id, links_data, links_diameter, links_length, links_roughness_coeff,
782
- links_bulk_coeff, links_wall_coeff, links_loss_coeff):
783
- links.append((link_id, link, {"diameter": diameter, "length": length,
784
- "roughness_coeff": roughness_coeff,
785
- "bulk_coeff": bulk_coeff, "wall_coeff": wall_coeff,
786
- "loss_coeff": loss_coeff}))
798
+ nodes.append((node_id, node_info))
787
799
 
788
- return NetworkTopology(f_inp=self.f_inp_in, nodes=nodes, links=links,
789
- units=self.get_units_category())
800
+ links = []
801
+ for link_id, link_type, link, diameter, length, roughness_coeff, bulk_coeff, wall_coeff, loss_coeff \
802
+ in zip(links_id, links_type, links_data, links_diameter, links_length,
803
+ links_roughness_coeff, links_bulk_coeff, links_wall_coeff, links_loss_coeff):
804
+ links.append((link_id, list(link),
805
+ {"type": link_type, "diameter": diameter, "length": length,
806
+ "roughness_coeff": roughness_coeff,
807
+ "bulk_coeff": bulk_coeff, "wall_coeff": wall_coeff,
808
+ "loss_coeff": loss_coeff}))
809
+
810
+ pumps = {}
811
+ for pump_id, pump_type in zip(pumps_id, pumps_type):
812
+ link_idx = links_id.index(pump_id)
813
+ link = links_data[link_idx]
814
+ pumps[pump_id] = {"type": pump_type, "end_points": link}
815
+
816
+ valves = {}
817
+ for valve_id in valves_id:
818
+ link_idx = links_id.index(valve_id)
819
+ link = links_data[link_idx]
820
+ valve_type = links_type[link_idx]
821
+ valves[valve_id] = {"type": valve_type, "end_points": link}
822
+
823
+ return NetworkTopology(f_inp=self.f_inp_in, nodes=nodes, links=links, pumps=pumps,
824
+ valves=valves, units=self.get_units_category())
790
825
 
791
826
  def randomize_demands(self) -> None:
792
827
  """
@@ -1739,8 +1774,7 @@ class ScenarioSimulator():
1739
1774
  # Fetch data
1740
1775
  pressure_data = self.epanet_api.getNodePressure().reshape(1, -1)
1741
1776
  flow_data = self.epanet_api.getLinkFlows().reshape(1, -1)
1742
- demand_data = self.epanet_api.getNodeActualDemand().reshape(1,
1743
- -1) # TODO: Does not go back after emitter coefficient is changed back to zero
1777
+ demand_data = self.epanet_api.getNodeActualDemand().reshape(1, -1)
1744
1778
  quality_node_data = self.epanet_api.getNodeActualQuality().reshape(1, -1)
1745
1779
  quality_link_data = self.epanet_api.getLinkActualQuality().reshape(1, -1)
1746
1780
  pumps_state_data = self.epanet_api.getLinkPumpState().reshape(1, -1)
@@ -1808,14 +1842,14 @@ class ScenarioSimulator():
1808
1842
 
1809
1843
  Parameters
1810
1844
  ----------
1811
- model_uncertainty : :class:`~epyt_flow.uncertainties.model_uncertainty.ModelUncertainty`
1845
+ model_uncertainty : :class:`~epyt_flow.uncertainty.model_uncertainty.ModelUncertainty`
1812
1846
  Model uncertainty specifications.
1813
1847
  """
1814
1848
  self.__adapt_to_network_changes()
1815
1849
 
1816
1850
  if not isinstance(model_uncertainty, ModelUncertainty):
1817
1851
  raise TypeError("'model_uncertainty' must be an instance of " +
1818
- "'epyt_flow.uncertainties.ModelUncertainty' but not of " +
1852
+ "'epyt_flow.uncertainty.ModelUncertainty' but not of " +
1819
1853
  f"'{type(model_uncertainty)}'")
1820
1854
 
1821
1855
  self.__model_uncertainty = model_uncertainty
@@ -1841,7 +1875,7 @@ class ScenarioSimulator():
1841
1875
  def set_general_parameters(self, demand_model: dict = None, simulation_duration: int = None,
1842
1876
  hydraulic_time_step: int = None, quality_time_step: int = None,
1843
1877
  reporting_time_step: int = None, reporting_time_start: int = None,
1844
- flow_units: int = None, quality_model: dict = None) -> None:
1878
+ flow_units_id: int = None, quality_model: dict = None) -> None:
1845
1879
  """
1846
1880
  Sets some general parameters.
1847
1881
 
@@ -1886,22 +1920,22 @@ class ScenarioSimulator():
1886
1920
  Start time (in seconds) at which reporting of hydraulic and quality states starts.
1887
1921
 
1888
1922
  The default is None.
1889
- flow_units : `int`, optional
1923
+ flow_units_id : `int`, optional
1890
1924
  Specifies the flow units -- i.e. all flows will be reported in these units.
1891
1925
  If None, the units from the .inp file will be used.
1892
1926
 
1893
1927
  Must be one of the following EPANET toolkit constants:
1894
1928
 
1895
- - EN_CFS = 0 (cu foot/sec)
1896
- - EN_GPM = 1 (gal/min)
1897
- - EN_MGD = 2 (Million gal/day)
1898
- - EN_IMGD = 3 (Imperial MGD)
1899
- - EN_AFD = 4 (ac-foot/day)
1900
- - EN_LPS = 5 (liter/sec)
1901
- - EN_LPM = 6 (liter/min)
1902
- - EN_MLD = 7 (Megaliter/day)
1903
- - EN_CMH = 8 (cubic meter/hr)
1904
- - EN_CMD = 9 (cubic meter/day)
1929
+ - EN_CFS = 0 (cubic foot/sec)
1930
+ - EN_GPM = 1 (gal/min)
1931
+ - EN_MGD = 2 (Million gal/day)
1932
+ - EN_IMGD = 3 (Imperial MGD)
1933
+ - EN_AFD = 4 (ac-foot/day)
1934
+ - EN_LPS = 5 (liter/sec)
1935
+ - EN_LPM = 6 (liter/min)
1936
+ - EN_MLD = 7 (Megaliter/day)
1937
+ - EN_CMH = 8 (cubic meter/hr)
1938
+ - EN_CMD = 9 (cubic meter/day)
1905
1939
 
1906
1940
  The default is None.
1907
1941
  quality_model : `dict`, optional
@@ -1922,8 +1956,7 @@ class ScenarioSimulator():
1922
1956
  if not isinstance(simulation_duration, int) or simulation_duration <= 0:
1923
1957
  raise ValueError("'simulation_duration' must be a positive integer specifying " +
1924
1958
  "the number of seconds to simulate")
1925
- self.epanet_api.setTimeSimulationDuration(simulation_duration) # TODO: Changing the simulation
1926
- # duration from .inp file seems to break EPANET-MSX
1959
+ self.epanet_api.setTimeSimulationDuration(simulation_duration)
1927
1960
 
1928
1961
  if hydraulic_time_step is not None:
1929
1962
  if not isinstance(hydraulic_time_step, int) or hydraulic_time_step <= 0:
@@ -1958,29 +1991,29 @@ class ScenarioSimulator():
1958
1991
  "greater than the hydraulic time step")
1959
1992
  self.epanet_api.setTimeQualityStep(quality_time_step)
1960
1993
 
1961
- if flow_units is not None:
1962
- if flow_units == ToolkitConstants.EN_CFS:
1994
+ if flow_units_id is not None:
1995
+ if flow_units_id == ToolkitConstants.EN_CFS:
1963
1996
  self.epanet_api.setFlowUnitsCFS()
1964
- elif flow_units == ToolkitConstants.EN_GPM:
1997
+ elif flow_units_id == ToolkitConstants.EN_GPM:
1965
1998
  self.epanet_api.setFlowUnitsGPM()
1966
- elif flow_units == ToolkitConstants.EN_MGD:
1999
+ elif flow_units_id == ToolkitConstants.EN_MGD:
1967
2000
  self.epanet_api.setFlowUnitsMGD()
1968
- elif flow_units == ToolkitConstants.EN_IMGD:
2001
+ elif flow_units_id == ToolkitConstants.EN_IMGD:
1969
2002
  self.epanet_api.setFlowUnitsIMGD()
1970
- elif flow_units == ToolkitConstants.EN_AFD:
2003
+ elif flow_units_id == ToolkitConstants.EN_AFD:
1971
2004
  self.epanet_api.setFlowUnitsAFD()
1972
- elif flow_units == ToolkitConstants.EN_LPS:
2005
+ elif flow_units_id == ToolkitConstants.EN_LPS:
1973
2006
  self.epanet_api.setFlowUnitsLPS()
1974
- elif flow_units == ToolkitConstants.EN_LPM:
2007
+ elif flow_units_id == ToolkitConstants.EN_LPM:
1975
2008
  self.epanet_api.setFlowUnitsLPM()
1976
- elif flow_units == ToolkitConstants.EN_MLD:
2009
+ elif flow_units_id == ToolkitConstants.EN_MLD:
1977
2010
  self.epanet_api.setFlowUnitsMLD()
1978
- elif flow_units == ToolkitConstants.EN_CMH:
2011
+ elif flow_units_id == ToolkitConstants.EN_CMH:
1979
2012
  self.epanet_api.setFlowUnitsCMH()
1980
- elif flow_units == ToolkitConstants.EN_CMD:
2013
+ elif flow_units_id == ToolkitConstants.EN_CMD:
1981
2014
  self.epanet_api.setFlowUnitsCMD()
1982
2015
  else:
1983
- raise ValueError(f"Unknown flow units '{flow_units}'")
2016
+ raise ValueError(f"Unknown flow units '{flow_units_id}'")
1984
2017
 
1985
2018
  if quality_model is not None:
1986
2019
  if quality_model["type"] == "NONE":
@@ -1989,7 +2022,7 @@ class ScenarioSimulator():
1989
2022
  self.epanet_api.setQualityType("age")
1990
2023
  elif quality_model["type"] == "CHEM":
1991
2024
  self.epanet_api.setQualityType("chem", quality_model["chemical_name"],
1992
- qualityunits_to_str(quality_model["units"]))
2025
+ qualityunit_to_str(quality_model["units"]))
1993
2026
  elif quality_model["type"] == "TRACE":
1994
2027
  self.epanet_api.setQualityType("trace", quality_model["trace_node_id"])
1995
2028
  else: