omotes-simulator-core 0.0.3.dev0__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 (59) hide show
  1. omotes_simulator_core-0.0.3.dev0.dist-info/LICENSE +621 -0
  2. omotes_simulator_core-0.0.3.dev0.dist-info/METADATA +55 -0
  3. omotes_simulator_core-0.0.3.dev0.dist-info/RECORD +59 -0
  4. omotes_simulator_core-0.0.3.dev0.dist-info/WHEEL +5 -0
  5. omotes_simulator_core-0.0.3.dev0.dist-info/top_level.txt +1 -0
  6. simulator_core/__init__.py +15 -0
  7. simulator_core/__main__.py +21 -0
  8. simulator_core/adapter/__init__.py +16 -0
  9. simulator_core/adapter/presentation/__init__.py +16 -0
  10. simulator_core/adapter/transforms/__init__.py +16 -0
  11. simulator_core/adapter/transforms/esdl_asset_mapper.py +53 -0
  12. simulator_core/adapter/transforms/mappers.py +226 -0
  13. simulator_core/adapter/transforms/string_to_esdl.py +66 -0
  14. simulator_core/adapter/transforms/transform_utils.py +34 -0
  15. simulator_core/entities/__init__.py +16 -0
  16. simulator_core/entities/assets/__init__.py +16 -0
  17. simulator_core/entities/assets/asset_abstract.py +116 -0
  18. simulator_core/entities/assets/asset_defaults.py +68 -0
  19. simulator_core/entities/assets/controller_classes.py +81 -0
  20. simulator_core/entities/assets/demand_cluster.py +117 -0
  21. simulator_core/entities/assets/esdl_asset_object.py +60 -0
  22. simulator_core/entities/assets/junction.py +64 -0
  23. simulator_core/entities/assets/pipe.py +174 -0
  24. simulator_core/entities/assets/production_cluster.py +222 -0
  25. simulator_core/entities/assets/utils.py +154 -0
  26. simulator_core/entities/esdl_object.py +90 -0
  27. simulator_core/entities/heat_network.py +82 -0
  28. simulator_core/entities/network_controller.py +67 -0
  29. simulator_core/entities/simulation_configuration.py +31 -0
  30. simulator_core/entities/utility/__init__.py +16 -0
  31. simulator_core/entities/utility/influxdb_reader.py +129 -0
  32. simulator_core/infrastructure/__init__.py +16 -0
  33. simulator_core/infrastructure/app.py +55 -0
  34. simulator_core/infrastructure/simulation_manager.py +55 -0
  35. simulator_core/infrastructure/utils.py +45 -0
  36. simulator_core/py.typed +0 -0
  37. simulator_core/simulation/__init__.py +18 -0
  38. simulator_core/simulation/mappers/__init__.py +16 -0
  39. simulator_core/simulation/mappers/mappers.py +35 -0
  40. simulator_core/simulation/networksimulation.py +63 -0
  41. simulator_core/solver/__init__.py +15 -0
  42. simulator_core/solver/matrix/__init__.py +15 -0
  43. simulator_core/solver/matrix/core_enum.py +26 -0
  44. simulator_core/solver/matrix/equation_object.py +56 -0
  45. simulator_core/solver/matrix/matrix.py +125 -0
  46. simulator_core/solver/matrix/utility.py +38 -0
  47. simulator_core/solver/network/__init__.py +15 -0
  48. simulator_core/solver/network/assets/__init__.py +15 -0
  49. simulator_core/solver/network/assets/base_asset.py +303 -0
  50. simulator_core/solver/network/assets/base_item.py +63 -0
  51. simulator_core/solver/network/assets/base_node_item.py +67 -0
  52. simulator_core/solver/network/assets/boundary.py +83 -0
  53. simulator_core/solver/network/assets/fall_type.py +225 -0
  54. simulator_core/solver/network/assets/node.py +274 -0
  55. simulator_core/solver/network/assets/production_asset.py +171 -0
  56. simulator_core/solver/network/assets/solver_pipe.py +541 -0
  57. simulator_core/solver/network/network.py +398 -0
  58. simulator_core/solver/solver.py +80 -0
  59. simulator_core/solver/utils/fluid_properties.py +173 -0
@@ -0,0 +1,116 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Abstract class for asset."""
17
+
18
+ from abc import ABC, abstractmethod
19
+ from typing import Dict, List
20
+
21
+ from pandas import DataFrame
22
+
23
+ from simulator_core.entities.assets.esdl_asset_object import EsdlAssetObject
24
+ from simulator_core.entities.assets.junction import Junction
25
+ from simulator_core.solver.network.assets.base_asset import BaseAsset
26
+
27
+
28
+ class AssetAbstract(ABC):
29
+ """Abstract class for Asset."""
30
+
31
+ from_junction: Junction | None
32
+ """The junction where the asset starts."""
33
+
34
+ to_junction: Junction | None
35
+ """The junction where the asset ends."""
36
+
37
+ name: str
38
+ """The name of the asset."""
39
+
40
+ asset_id: str
41
+ """The unique identifier of the asset."""
42
+
43
+ output: List[Dict[str, float]]
44
+ """The output of the asset as a list with a dictionary per timestep."""
45
+ solver_asset: BaseAsset
46
+
47
+ def __init__(self, asset_name: str, asset_id: str):
48
+ """Basic constructor for asset objects.
49
+
50
+ :param str asset_name: The name of the asset.
51
+ :param str asset_id: The unique identifier of the asset.
52
+ :param PandapipesNet pandapipe_net: Pnadapipes network object to register asset to.
53
+ """
54
+ self.from_junction = None
55
+ self.to_junction: Junction = None
56
+ self.name: str = asset_name
57
+ self.asset_id: str = asset_id
58
+ self.output: List[Dict[str, float]] = []
59
+
60
+ @abstractmethod
61
+ def set_setpoints(self, setpoints: Dict) -> None:
62
+ """Placeholder to set the setpoints of an asset prior to a simulation.
63
+
64
+ :param Dict setpoints: The setpoints that should be set for the asset.
65
+ The keys of the dictionary are the names of the setpoints and the values are the values
66
+ """
67
+
68
+ def get_setpoints(self) -> Dict[str, float]:
69
+ """Placeholder to get the setpoint attributes of an asset.
70
+
71
+ :return Dict: The setpoints of the asset. The keys of the dictionary are the names of the
72
+ setpoints and the values are the values.
73
+ """
74
+ return {}
75
+
76
+ @abstractmethod
77
+ def add_physical_data(self, esdl_asset: EsdlAssetObject) -> None:
78
+ """Placeholder method to add physical data to an asset."""
79
+
80
+ def set_from_junction(self, from_junction: Junction) -> None:
81
+ """Method to set the from junction of an asset.
82
+
83
+ :param Junction from_junction: The junction where the asset starts.
84
+ """
85
+ self.from_junction = from_junction
86
+
87
+ def set_to_junction(self, to_junction: Junction) -> None:
88
+ """Method to set the to junction of an asset.
89
+
90
+ :param Junction to_junction: The junction where the asset ends.
91
+ """
92
+ self.to_junction = to_junction
93
+
94
+ @abstractmethod
95
+ def write_to_output(self) -> None:
96
+ """Placeholder to get data from pandapipes and store it in the asset."""
97
+
98
+ def get_output(self) -> List[Dict[str, float]]:
99
+ """Returns all the output of the asset.
100
+
101
+ :return: A dict of property name and list of values.
102
+ """
103
+ return self.output
104
+
105
+ def get_timeseries(self) -> DataFrame:
106
+ """Get timeseries as a dataframe from a pandapipes asset.
107
+
108
+ The header is a tuple of the asset id and the property name.
109
+ """
110
+ # Create dataframe
111
+ temp_dataframe = DataFrame(self.output)
112
+ # Set header
113
+ temp_dataframe.columns = [
114
+ (self.asset_id, column_name) for column_name in temp_dataframe.columns
115
+ ]
116
+ return temp_dataframe
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Define default values and names for assets."""
17
+ from dataclasses import dataclass
18
+
19
+ # Default values
20
+ DEFAULT_DIAMETER = 1.2 # [m]
21
+ DEFAULT_PRESSURE = 10.0e5 # [bar]
22
+ DEFAULT_PRESSURE_DIFFERENCE = 5e5 # [bar]
23
+ DEFAULT_TEMPERATURE = 300.0 # [K]
24
+ DEFAULT_TEMPERATURE_DIFFERENCE = 30.0 # [K]
25
+ DEFAULT_NODE_HEIGHT = 0.0 # [m]
26
+ DEFAULT_MASS_FLOW_RATE = 1.0 # [kg/s]
27
+ DEFAULT_POWER = 500000.0 # [W]
28
+ DEFAULT_MISSING_VALUE = -9999.99 # [-]
29
+
30
+
31
+ @dataclass
32
+ class PipeDefaults:
33
+ """Class containing the default values for a pipe.
34
+
35
+ :param float k_value: The k value of the pipe [m].
36
+ :param float alpha_value: The alpha value of the pipe [W/(m2 K)].
37
+ :param float minor_loss_coefficient: The minor loss coefficient of the pipe [-].
38
+ :param float external_temperature: The external temperature of the pipe [K].
39
+ :param float qheat_external: The external heat flow of the pipe [W].
40
+ """
41
+
42
+ k_value: float = 2e-3
43
+ alpha_value: float = 0.0
44
+ minor_loss_coefficient: float = 0.0
45
+ external_temperature: float = 273.15 + 20.0
46
+ qheat_external: float = 0.0
47
+ length: float = 1.0
48
+ diameter: float = DEFAULT_DIAMETER
49
+
50
+
51
+ # Default names
52
+ PROPERTY_HEAT_DEMAND = "heat_demand"
53
+ PROPERTY_TEMPERATURE_SUPPLY = "temperature_supply"
54
+ PROPERTY_TEMPERATURE_RETURN = "temperature_return"
55
+ PROPERTY_PRESSURE_SUPPLY = "pressure_supply"
56
+ PROPERTY_PRESSURE_RETURN = "pressure_return"
57
+ PROPERTY_MASSFLOW = "mass_flow"
58
+ PROPERTY_VOLUMEFLOW = "volume_flow"
59
+ PROPERTY_THERMAL_POWER = "thermal_power"
60
+ PROPERTY_VELOCITY_SUPPLY = "velocity_supply"
61
+ PROPERTY_VELOCITY_RETURN = "velocity_return"
62
+ PROPERTY_SET_PRESSURE = "set_pressure"
63
+ PROPERTY_LENGTH = "length"
64
+ PROPERTY_DIAMETER = "diameter"
65
+ PROPERTY_ROUGHNESS = "roughness"
66
+
67
+ # Static members
68
+ PIPE_DEFAULTS = PipeDefaults()
@@ -0,0 +1,81 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ """Module containing the classes for the controller."""
16
+
17
+ import datetime
18
+
19
+ import pandas as pd
20
+ from simulator_core.entities.assets.asset_defaults import (DEFAULT_TEMPERATURE,
21
+ DEFAULT_TEMPERATURE_DIFFERENCE)
22
+
23
+
24
+ class AssetControllerAbstract:
25
+ """Abstract class for the asset controller."""
26
+
27
+ def __init__(self, name: str, identifier: str):
28
+ """Constructor for the asset controller.
29
+
30
+ :param str name: Name of the controller object.
31
+ :param str identifier: Unique identifier of the controller asset.
32
+ """
33
+ self.name = name
34
+ self.id = identifier
35
+
36
+
37
+ class ControllerConsumer(AssetControllerAbstract):
38
+ """Class to store the consumer for the controller asset."""
39
+
40
+ def __init__(self, name: str, identifier: str):
41
+ """Constructor for the consumer.
42
+
43
+ :param str name: Name of the consumer.
44
+ :param str identifier: Unique identifier of the consumer.
45
+ """
46
+ super().__init__(name, identifier)
47
+ self.temperature_return = DEFAULT_TEMPERATURE
48
+ self.temperature_supply = DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE
49
+ self.profile: pd.DataFrame = pd.DataFrame()
50
+ self.start_index = 0
51
+
52
+ def get_heat_demand(self, time: datetime.datetime) -> float:
53
+ """Method to get the heat demand of the consumer.
54
+
55
+ :param datetime.datetime time: Time for which to get the heat demand.
56
+ :return: float with the heat demand.
57
+ """
58
+ for index in range(self.start_index, len(self.profile)):
59
+ if abs((self.profile["date"][index].to_pydatetime() - time).total_seconds()) < 3600:
60
+ self.start_index = index
61
+ return float(self.profile["values"][index])
62
+ return 0
63
+
64
+ def add_profile(self, profile: pd.DataFrame) -> None:
65
+ """Method to add a profile to the consumer."""
66
+ self.profile = profile
67
+
68
+
69
+ class ControllerSource(AssetControllerAbstract):
70
+ """Class to store the source for the controller."""
71
+
72
+ def __init__(self, name: str, identifier: str):
73
+ """Constructor for the source.
74
+
75
+ :param str name: Name of the source.
76
+ :param str identifier: Unique identifier of the source.
77
+ """
78
+ super().__init__(name, identifier)
79
+ self.temperature_return: float = DEFAULT_TEMPERATURE
80
+ self.temperature_supply: float = DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE
81
+ self.power: float = 5000000000
@@ -0,0 +1,117 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """demandCluster class."""
17
+ import uuid
18
+ from typing import Dict
19
+
20
+ from simulator_core.entities.assets.asset_abstract import AssetAbstract
21
+ from simulator_core.entities.assets.asset_defaults import (
22
+ DEFAULT_DIAMETER,
23
+ DEFAULT_POWER,
24
+ DEFAULT_PRESSURE,
25
+ DEFAULT_TEMPERATURE,
26
+ DEFAULT_TEMPERATURE_DIFFERENCE,
27
+ PROPERTY_HEAT_DEMAND,
28
+ PROPERTY_MASSFLOW,
29
+ PROPERTY_PRESSURE_RETURN,
30
+ PROPERTY_PRESSURE_SUPPLY,
31
+ PROPERTY_TEMPERATURE_RETURN,
32
+ PROPERTY_TEMPERATURE_SUPPLY,
33
+ )
34
+ from simulator_core.entities.assets.esdl_asset_object import EsdlAssetObject
35
+ from simulator_core.entities.assets.utils import (
36
+ heat_demand_and_temperature_to_mass_flow,
37
+ )
38
+ from simulator_core.solver.network.assets.production_asset import ProductionAsset
39
+
40
+
41
+ class DemandCluster(AssetAbstract):
42
+ """A DemandCluster represents an asset that consumes heat."""
43
+
44
+ def __init__(
45
+ self,
46
+ asset_name: str,
47
+ asset_id: str,
48
+ ):
49
+ """Initialize a DemandCluster object.
50
+
51
+ :param str asset_name: The name of the asset.
52
+ :param str asset_id: The unique identifier of the asset.
53
+ :param PandapipesNet pandapipe_net: Pandapipes network object to register asset to.
54
+ """
55
+ super().__init__(asset_name=asset_name, asset_id=asset_id)
56
+
57
+ self._internal_diameter = DEFAULT_DIAMETER
58
+ self.temperature_supply = DEFAULT_TEMPERATURE
59
+ self.temperature_return: float = DEFAULT_TEMPERATURE - DEFAULT_TEMPERATURE_DIFFERENCE
60
+ self.temperature_return_target = self.temperature_return
61
+ self.pressure_input = DEFAULT_PRESSURE
62
+ self.thermal_power_allocation = DEFAULT_POWER
63
+ self.mass_flowrate = 0
64
+ self.solver_asset = ProductionAsset(uuid.uuid4())
65
+ # Output list
66
+ self.output: list = []
67
+
68
+ def set_setpoints(self, setpoints: Dict) -> None:
69
+ """Placeholder to set the setpoints of an asset prior to a simulation.
70
+
71
+ :param Dict setpoints: The setpoints that should be set for the asset.
72
+ The keys of the dictionary are the names of the setpoints and the values are the values
73
+ """
74
+ # Default keys required
75
+ necessary_setpoints = {
76
+ PROPERTY_TEMPERATURE_SUPPLY,
77
+ PROPERTY_TEMPERATURE_RETURN,
78
+ PROPERTY_HEAT_DEMAND,
79
+ }
80
+ # Dict to set
81
+ setpoints_set = set(setpoints.keys())
82
+ # Check if all setpoints are in the setpoints
83
+ if not necessary_setpoints.issubset(setpoints_set):
84
+ # Print missing setpoints
85
+ raise ValueError(
86
+ f"The setpoints {necessary_setpoints.difference(setpoints_set)} are missing."
87
+ )
88
+ self.thermal_power_allocation = setpoints[PROPERTY_HEAT_DEMAND]
89
+ self.temperature_return_target = setpoints[PROPERTY_TEMPERATURE_RETURN]
90
+ self.temperature_supply = setpoints[PROPERTY_TEMPERATURE_SUPPLY]
91
+ adjusted_mass_flowrate = heat_demand_and_temperature_to_mass_flow(
92
+ self.thermal_power_allocation, self.temperature_supply, self.temperature_return_target
93
+ )
94
+ self.solver_asset.supply_temperature = self.temperature_return_target
95
+ self.solver_asset.mass_flow_rate_set_point = adjusted_mass_flowrate # type: ignore
96
+
97
+ def add_physical_data(self, esdl_asset: EsdlAssetObject) -> None:
98
+ """Method to add physical data to the asset.
99
+
100
+ :param EsdlAssetObject esdl_asset: The esdl asset object to add the physical data from.
101
+ :return:
102
+ """
103
+
104
+ def write_to_output(self) -> None:
105
+ """Placeholder to write the asset to the output.
106
+
107
+ The output list is a list of dictionaries, where each dictionary
108
+ represents the output of its asset for a specific timestep.
109
+ """
110
+ output_dict = {
111
+ PROPERTY_MASSFLOW: self.solver_asset.get_mass_flow_rate(1),
112
+ PROPERTY_PRESSURE_SUPPLY: self.solver_asset.get_pressure(0),
113
+ PROPERTY_PRESSURE_RETURN: self.solver_asset.get_pressure(1),
114
+ PROPERTY_TEMPERATURE_SUPPLY: self.solver_asset.get_temperature(0),
115
+ PROPERTY_TEMPERATURE_RETURN: self.solver_asset.get_temperature(1),
116
+ }
117
+ self.output.append(output_dict)
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Module containing classes to be able to interact with esdl objects."""
17
+ import logging
18
+ from typing import Any, Tuple
19
+
20
+ import pandas as pd
21
+ from esdl import esdl
22
+ from simulator_core.entities.utility.influxdb_reader import get_data_from_profile
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class EsdlAssetObject:
28
+ """
29
+ Class to hold an esdl asset and convert it to local class objects.
30
+
31
+ Conversion is done based on the classes in the conversion_dict.
32
+ """
33
+
34
+ esdl_asset: esdl.Asset
35
+
36
+ def __init__(self, asset: esdl.Asset) -> None:
37
+ """
38
+ Constructor for EsdlAssetObject class.
39
+
40
+ :param asset, esdl.Asset: PyEsdl Asset object
41
+ """
42
+ self.esdl_asset = asset
43
+
44
+ def get_property(self, esdl_property_name: str, default_value: Any) -> Tuple[Any, bool]:
45
+ """Get property value from the esdl_asset based on the "ESDL" name.
46
+
47
+ :return: Tuple with the value of the property and a boolean indicating whether the property
48
+ was found in the esdl_asset.
49
+ """
50
+ try:
51
+ return getattr(self.esdl_asset, esdl_property_name), True
52
+ except AttributeError:
53
+ return default_value, False
54
+
55
+ def get_profile(self) -> pd.DataFrame:
56
+ """Get the profile of the asset."""
57
+ for esdl_port in self.esdl_asset.port:
58
+ if esdl_port.profile:
59
+ return get_data_from_profile(esdl_port.profile[0])
60
+ raise ValueError("No profile found for asset: " + self.esdl_asset.name)
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """Junction classes."""
17
+
18
+ from typing import Any, List, Optional
19
+
20
+ from simulator_core.entities.assets.asset_defaults import (DEFAULT_NODE_HEIGHT,
21
+ DEFAULT_PRESSURE,
22
+ DEFAULT_TEMPERATURE)
23
+ from simulator_core.solver.network.assets.node import Node
24
+
25
+
26
+ class Junction:
27
+ """Wrapper class for pandapipes junctions."""
28
+
29
+ def __init__(
30
+ self,
31
+ solver_node: Node,
32
+ pn_bar: float = DEFAULT_PRESSURE,
33
+ tfluid_k: float = DEFAULT_TEMPERATURE,
34
+ height_m: float = DEFAULT_NODE_HEIGHT,
35
+ geodata: Optional[List[Any]] = None,
36
+ name: str = "None",
37
+ in_service: bool = True,
38
+ index: Optional[int] = None,
39
+ ):
40
+ """Initialize a Junction object.
41
+
42
+ :param pn_bar: The pressure at the junction [bar], defaults to DEFAULT_PRESSURE
43
+ :type pn_bar: float, optional
44
+ :param tfluid_k: The temperature at the junction [K], defaults to DEFAULT_TEMPERATURE
45
+ :type tfluid_k: float, optional
46
+ :param height_m: The height of the junction [m], defaults to DEFAULT_NODE_HEIGHT
47
+ :type height_m: float, optional
48
+
49
+ """
50
+ self.pn_bar = pn_bar
51
+ self.tfluid_k = tfluid_k
52
+ self.height_m = height_m
53
+ self.name = name
54
+ self.index = index
55
+ # Initialize the junction
56
+ self.solver_junction = solver_node
57
+
58
+ def write_to_output(self) -> None:
59
+ """Placeholder to write the asset to the output.
60
+
61
+ The output list is a list of dictionaries, where each dictionary
62
+ represents the output of its asset for a specific timestep.
63
+ """
64
+ return
@@ -0,0 +1,174 @@
1
+ # Copyright (c) 2023. Deltares & TNO
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+ """Module containing pipe class."""
16
+ import uuid
17
+ from typing import Dict, List
18
+
19
+ import numpy as np
20
+
21
+ from simulator_core.entities.assets.asset_abstract import AssetAbstract
22
+ from simulator_core.entities.assets.asset_defaults import (
23
+ PIPE_DEFAULTS,
24
+ PROPERTY_MASSFLOW,
25
+ PROPERTY_PRESSURE_RETURN,
26
+ PROPERTY_PRESSURE_SUPPLY,
27
+ PROPERTY_TEMPERATURE_RETURN,
28
+ PROPERTY_TEMPERATURE_SUPPLY,
29
+ )
30
+ from simulator_core.entities.assets.esdl_asset_object import EsdlAssetObject
31
+ from simulator_core.entities.assets.utils import (
32
+ calculate_inverse_heat_transfer_coefficient,
33
+ get_thermal_conductivity_table,
34
+ )
35
+ from simulator_core.solver.network.assets.solver_pipe import SolverPipe
36
+
37
+
38
+ class Pipe(AssetAbstract):
39
+ """A class representing a pipe in a heat network."""
40
+
41
+ _minor_loss_coefficient: float
42
+ """The minor loss coefficient of the pipe [-]."""
43
+ _external_temperature: float
44
+ """The external temperature surrounding the pipe [K]."""
45
+ _qheat_external: float
46
+ """The external heat flow into the pipe [W]."""
47
+ length: float
48
+ """The length of the pipe [m]."""
49
+ diameter: float
50
+ """The diameter of the pipe [m]."""
51
+ roughness: float
52
+ """The wall roughness of the pipe [m]."""
53
+ alpha_value: float
54
+ """The alpha value of the pipe [W/(m2 K)]."""
55
+ _initialized: bool
56
+ """Flag to indicate whether the pipe has been initialized in pandapipes."""
57
+ output: List[Dict[str, float]]
58
+ """The output list of the pipe with a dictionaries for each timestep."""
59
+
60
+ def __init__(self, asset_name: str, asset_id: str):
61
+ """Initialize a Pipe object.
62
+
63
+ :param str asset_name: The name of the asset.
64
+ :param str asset_id: The unique identifier of the asset.
65
+ :param PandapipesNet pandapipe_net: Pandapipes network object to register asset to.
66
+ """
67
+ super().__init__(asset_name=asset_name, asset_id=asset_id)
68
+ # Initialize the default values of the pipe
69
+ self._minor_loss_coefficient = PIPE_DEFAULTS.minor_loss_coefficient
70
+ self._external_temperature = PIPE_DEFAULTS.external_temperature
71
+ self._qheat_external = PIPE_DEFAULTS.qheat_external
72
+ # Define properties of the pipe
73
+ self.length = PIPE_DEFAULTS.length
74
+ self.diameter = PIPE_DEFAULTS.diameter
75
+ self.roughness = PIPE_DEFAULTS.k_value
76
+ self.alpha_value = PIPE_DEFAULTS.alpha_value
77
+ # Objects of the pandapipes network
78
+ self.solver_asset = SolverPipe(
79
+ uuid.uuid4(), length=self.length, diameter=self.diameter, roughness=self.roughness
80
+ )
81
+ self.output = []
82
+
83
+ def _get_diameter(self, esdl_asset: EsdlAssetObject) -> float:
84
+ """Retrieve the diameter of the pipe and convert it if necessary."""
85
+ temp_diameter, property_available = esdl_asset.get_property("innerDiameter", self.diameter)
86
+ if property_available:
87
+ if temp_diameter == 0:
88
+ return self.diameter
89
+ return float(temp_diameter)
90
+ else:
91
+ # Implement DN-conversion
92
+ raise NotImplementedError(
93
+ f"The innderDiamter property is unavailable for {esdl_asset.esdl_asset.name}. \
94
+ Conversion from DN to diameter is not yet implemented."
95
+ )
96
+
97
+ def _get_heat_transfer_coefficient(self, esdl_asset: EsdlAssetObject) -> float:
98
+ """Calculate the heat transfer coefficient of the pipe.
99
+
100
+ :param EsdlAssetObject esdl_asset: The ESDL asset object associated with the
101
+ current pipe object.
102
+
103
+ :return: The heat transfer coefficient of the pipe [W/(m2 K)]. If the heat transfer
104
+ coefficient cannot be calculated - for example when the material table is
105
+ not specified - , the default alpha value is returned.
106
+ """
107
+ diameters, heat_coefficients = get_thermal_conductivity_table(esdl_asset=esdl_asset)
108
+ if diameters:
109
+ # Create a numpy array of the diameters and heat coefficients
110
+ diameters_np = np.array(diameters)
111
+ heat_coefficients_np = np.array(heat_coefficients)
112
+ # Calculate the heat transfer coefficient from the heat transfer table
113
+ inverse_heat_transfer_coefficient = np.sum(
114
+ calculate_inverse_heat_transfer_coefficient(
115
+ inner_diameter=diameters_np[:-1],
116
+ outer_diameter=diameters_np[1:],
117
+ thermal_conductivity=heat_coefficients_np,
118
+ )
119
+ )
120
+ return 1.0 / float(inverse_heat_transfer_coefficient)
121
+ else:
122
+ return self.alpha_value
123
+
124
+ def add_physical_data(self, esdl_asset: EsdlAssetObject) -> None:
125
+ """Method to add physical data to the asset.
126
+
127
+ :param EsdlAssetObject esdl_asset: The ESDL asset object associated with the
128
+ current pipe object.
129
+ """
130
+ # Error handling is performed in EsdlAssetObject.get_asset_parameters
131
+ self.length, _ = esdl_asset.get_property(
132
+ esdl_property_name="length", default_value=self.length
133
+ )
134
+ self.roughness, _ = esdl_asset.get_property(
135
+ esdl_property_name="roughness", default_value=self.roughness
136
+ )
137
+ self.roughness = PIPE_DEFAULTS.k_value if self.roughness == 0 else self.roughness
138
+ self.diameter = self._get_diameter(esdl_asset=esdl_asset)
139
+
140
+ self.alpha_value = self._get_heat_transfer_coefficient(esdl_asset=esdl_asset)
141
+ prop_dict = {"length": self.length, "diameter": self.diameter, "roughness": self.roughness}
142
+ self.solver_asset.set_physical_properties(physical_properties=prop_dict)
143
+
144
+ def set_setpoints(self, setpoints: Dict) -> None:
145
+ """Set the setpoints of the pipe prior to a simulation.
146
+
147
+ :param Dict setpoints: The setpoints that should be set for the pipe.
148
+ The keys of the dictionary are the names of the setpoints and the values are the values
149
+ """
150
+
151
+ def write_to_output(self) -> None:
152
+ """Write the output of the asset to the output list.
153
+
154
+ The output list is a list of dictionaries, where each dictionary
155
+ represents the output of its asset for a specific timestep.
156
+
157
+ The output of the asset is a dictionary with the following keys:
158
+ - PROPERTY_HEAT_DEMAND: The heat demand of the asset.
159
+ - PROPERTY_TEMPERATURE_SUPPLY: The supply temperature of the asset.
160
+ - PROPERTY_TEMPERATURE_RETURN: The return temperature of the asset.
161
+ - PROPERTY_PRESSURE_SUPPLY: The supply pressure of the asset.
162
+ - PROPERTY_PRESSURE_RETURN: The return pressure of the asset.
163
+ - PROPERTY_MASSFLOW: The mass flow rate of the asset.
164
+ - PROPERTY_VELOCITY_SUPPLY: The supply velocity of the asset.
165
+ - PROPERTY_VELOCITY_RETURN: The return velocity of the asset.
166
+ """
167
+ output_dict = {
168
+ PROPERTY_MASSFLOW: self.solver_asset.get_mass_flow_rate(1),
169
+ PROPERTY_PRESSURE_SUPPLY: self.solver_asset.get_pressure(0),
170
+ PROPERTY_PRESSURE_RETURN: self.solver_asset.get_pressure(1),
171
+ PROPERTY_TEMPERATURE_SUPPLY: self.solver_asset.get_temperature(0),
172
+ PROPERTY_TEMPERATURE_RETURN: self.solver_asset.get_temperature(1),
173
+ }
174
+ self.output.append(output_dict)