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,83 @@
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 Based boundary class."""
17
+ import uuid
18
+
19
+ import numpy as np
20
+
21
+ from simulator_core.solver.matrix.core_enum import IndexEnum
22
+ from simulator_core.solver.matrix.equation_object import EquationObject
23
+ from simulator_core.solver.network.assets.base_asset import BaseAsset
24
+
25
+
26
+ class BaseBoundary(BaseAsset):
27
+ """A base class for boundary conditions.
28
+
29
+ This class represents a boundary condition and
30
+ has a number of connection points, a name, and a number of unknowns.
31
+
32
+ Attributes:
33
+ connected_nodes (list): A list of nodes that are connected to this boundary.
34
+ number_of_connection_point (int): The number of connection points for this boundary.
35
+ equations_dict (dict): A dictionary that maps equation ids to equation indices in the
36
+ """
37
+
38
+ def __init__(self, name: uuid.UUID, number_of_unknowns: int = 3, number_con_points: int = 1):
39
+ """Initialize the boundary condition.
40
+
41
+ :param int number_con_points:The number of connection points for this boundary.
42
+ :param str name: The name of this boundary.
43
+ :param int number_of_unknowns (optional): The number of unknowns for this boundary.
44
+ Defaults to 3.
45
+ """
46
+ super().__init__(name, number_of_unknowns, number_con_points)
47
+ self.number_of_connection_point = number_con_points
48
+ self.initial_pressure = 10000.0
49
+
50
+ def add_pressure_equation(self) -> EquationObject:
51
+ """Add a prescribed pressure equation for the boundary.
52
+
53
+ This method adds an equation to the matrix that sets the pressure at the boundary
54
+ to a fixed value.
55
+ :return: EquationObject
56
+ An EquationObject that contains the indices, coefficients, and right-hand side
57
+ value of the equation.
58
+ """
59
+ # self.equations_dict["prescribe pressure"] = equation_id
60
+ equation_object = EquationObject()
61
+ equation_object.indices = np.array([self.matrix_index + IndexEnum.pressure])
62
+ equation_object.coefficients = np.array([1.0])
63
+ equation_object.rhs = self.initial_pressure
64
+ return equation_object
65
+
66
+ def get_equations(self) -> list[EquationObject]:
67
+ """Method to get all the equations of the boundary.
68
+
69
+ These equations are:
70
+ - Pressure balance at each connection point
71
+ - Thermal balance at each connection point
72
+ - Pre describe pressure equation
73
+
74
+ :return:EquationObject
75
+ An EquationObject that contains the indices, coefficients, and right-hand side
76
+ value of the equation.
77
+ """
78
+ equations = [
79
+ self.add_pressure_equation(),
80
+ self.add_thermal_equations(0),
81
+ self.add_press_to_node_equation(0),
82
+ ]
83
+ return equations
@@ -0,0 +1,225 @@
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 the Fall type class."""
17
+ import uuid
18
+
19
+ import numpy as np
20
+
21
+ from simulator_core.solver.matrix.core_enum import NUMBER_CORE_QUANTITIES, IndexEnum
22
+ from simulator_core.solver.matrix.equation_object import EquationObject
23
+ from simulator_core.solver.network.assets.base_asset import BaseAsset
24
+
25
+
26
+ class FallType(BaseAsset):
27
+ """A class to represent a fall type asset in a network.
28
+
29
+ This class inherits from the BaseAsset class and implements the methods to generate the
30
+ equations for the fall type asset.
31
+
32
+ Attributes
33
+ ----------
34
+ connected_nodes : list[int]
35
+ A list of integers that store the node indices that are connected to the asset.
36
+ number_of_connection_point : int
37
+ The number of connection points for the asset.
38
+
39
+ Methods
40
+ -------
41
+ get_equations() -> list[EquationObject]
42
+ Returns a list of EquationObjects that represent the equations for the asset.
43
+ add_equations()
44
+ Adds the equations for the asset to the equation system.
45
+ add_internal_cont_equation() -> EquationObject
46
+ Returns an EquationObject that represents the internal continuity equation for the asset.
47
+ add_internal_pressure_loss_equation() -> EquationObject
48
+ Returns an EquationObject that represents the internal pressure loss equation for the asset.
49
+ add_internal_energy_equation() -> EquationObject
50
+ Returns an EquationObject that represents the internal energy equation for the asset.
51
+ update_pressure_loss_equation() -> EquationObject
52
+ Returns an updated EquationObject that represents the internal pressure loss equation
53
+ for the asset.
54
+ """
55
+
56
+ def __init__(
57
+ self,
58
+ name: uuid.UUID,
59
+ supply_temperature: float = 293.15,
60
+ heat_supplied: float = 0.0,
61
+ loss_coefficient: float = 1.0,
62
+ ):
63
+ """
64
+ Initializes the FallType object with the given parameters.
65
+
66
+ Parameters
67
+ ----------
68
+ name : uuid.UUID
69
+ The unique identifier of the asset.
70
+ number_of_unknowns : int, optional
71
+ The number of unknown variables for the asset. The default is 6, which corresponds
72
+ to the mass flow rate, pressure, and temperature at each connection point.
73
+ number_con_points : int, optional
74
+ The number of connection points for the asset. The default is 2, which corresponds to
75
+ the inlet and outlet.
76
+ """
77
+ super().__init__(
78
+ name=name,
79
+ number_of_unknowns=NUMBER_CORE_QUANTITIES * 2,
80
+ number_connection_points=2,
81
+ supply_temperature=supply_temperature,
82
+ )
83
+ self.heat_supplied = heat_supplied
84
+ self.loss_coefficient = loss_coefficient
85
+
86
+ def get_equations(self) -> list[EquationObject]:
87
+ """Returns a list of EquationObjects that represent the equations for the asset.
88
+
89
+ The equations are:
90
+
91
+ - Pressure balance at each connection point
92
+ - Thermal balance at each connection point
93
+ - Internal continuity equation
94
+ - Internal pressure loss equation
95
+ :return: list[EquationObject]
96
+ A list of EquationObjects that contain the indices, coefficients, and right-hand side
97
+ values of the equations.
98
+ """
99
+ # Check if there are two nodes connected to the asset
100
+ if len(self.connected_nodes) != 2:
101
+ raise ValueError("The number of connected nodes must be 2!")
102
+ # Check if the number of unknowns is 6
103
+ if self.number_of_unknowns != 6:
104
+ raise ValueError("The number of unknowns must be 6!")
105
+ # Add the equations for the asset
106
+ equations = [
107
+ self.add_press_to_node_equation(0),
108
+ self.add_press_to_node_equation(1),
109
+ self.add_thermal_equations(0),
110
+ self.add_thermal_equations(1),
111
+ self.add_internal_cont_equation(),
112
+ self.add_internal_pressure_loss_equation(),
113
+ ]
114
+ return equations
115
+
116
+ def add_thermal_equations(self, connection_point: int) -> EquationObject:
117
+ """Adds a thermal equation for a connection point of the asset.
118
+
119
+ :param connection_point: The index of the connection point to add the equation for.
120
+ :type connection_point: int
121
+ :return: An equation object representing the thermal equation.
122
+ :rtype: EquationObject
123
+ """
124
+ if self.prev_sol[IndexEnum.discharge + connection_point * NUMBER_CORE_QUANTITIES] > 0:
125
+ return self.add_internal_energy_equation()
126
+ else:
127
+ return self.add_temp_to_node_equation(connection_point=connection_point)
128
+
129
+ def add_internal_cont_equation(self) -> EquationObject:
130
+ """Returns an EquationObject that represents the internal continuity equation for the asset.
131
+
132
+ :return: EquationObject
133
+ An EquationObject that contains the indices, coefficients, and right-hand side value
134
+ of the equation.
135
+ """
136
+ equation_object = EquationObject()
137
+ equation_object.indices = np.array(
138
+ [
139
+ self.matrix_index + IndexEnum.discharge,
140
+ self.matrix_index + IndexEnum.discharge + NUMBER_CORE_QUANTITIES,
141
+ ]
142
+ )
143
+ equation_object.coefficients = np.array([1.0, 1.0])
144
+ equation_object.rhs = 0.0
145
+ return equation_object
146
+
147
+ def add_internal_energy_equation(self) -> EquationObject:
148
+ """Returns an EquationObject that represents the internal energy equation for the asset.
149
+
150
+ The equation is:
151
+
152
+ - Mass flow rate at inlet * Specific internal energy at inlet
153
+ + Mass flow rate at outlet * Specific internal energy at outlet - Heat supplied = 0
154
+
155
+ :return:EquationObject
156
+ An EquationObject that contains the indices, coefficients, and right-hand side value of
157
+ the equation.
158
+ """
159
+ equation_object = EquationObject()
160
+ self.update_heat_supplied()
161
+ equation_object.indices = np.array(
162
+ [
163
+ self.matrix_index + IndexEnum.discharge + NUMBER_CORE_QUANTITIES * 0,
164
+ self.matrix_index + IndexEnum.internal_energy + NUMBER_CORE_QUANTITIES * 0,
165
+ self.matrix_index + IndexEnum.discharge + NUMBER_CORE_QUANTITIES * 1,
166
+ self.matrix_index + IndexEnum.internal_energy + NUMBER_CORE_QUANTITIES * 1,
167
+ ]
168
+ )
169
+ equation_object.coefficients = np.array(
170
+ [
171
+ self.prev_sol[IndexEnum.internal_energy],
172
+ self.prev_sol[IndexEnum.discharge],
173
+ self.prev_sol[IndexEnum.internal_energy + NUMBER_CORE_QUANTITIES],
174
+ self.prev_sol[IndexEnum.discharge + NUMBER_CORE_QUANTITIES],
175
+ ]
176
+ )
177
+ equation_object.rhs = (
178
+ self.prev_sol[IndexEnum.discharge] * self.prev_sol[IndexEnum.internal_energy]
179
+ + self.prev_sol[IndexEnum.discharge + NUMBER_CORE_QUANTITIES]
180
+ * self.prev_sol[IndexEnum.internal_energy + NUMBER_CORE_QUANTITIES]
181
+ + self.heat_supplied
182
+ )
183
+ return equation_object
184
+
185
+ def add_internal_pressure_loss_equation(self) -> EquationObject:
186
+ """Returns an EquationObject that represents the pressure loss equation for the asset.
187
+
188
+ The equation is:
189
+
190
+ - Pressure at inlet - Pressure at outlet - 2 * Loss coefficient * Mass flow rate *
191
+ abs(Mass flow rate) = 0
192
+ :return: EquationObject
193
+ An EquationObject that contains the indices, coefficients, and right-hand side value
194
+ of the equation.
195
+ """
196
+ equation_object = EquationObject()
197
+ equation_object.indices = np.array(
198
+ [
199
+ self.matrix_index + IndexEnum.discharge,
200
+ self.matrix_index + IndexEnum.pressure,
201
+ self.matrix_index + IndexEnum.pressure + NUMBER_CORE_QUANTITIES,
202
+ ]
203
+ )
204
+ self.update_loss_coefficient()
205
+ if self.prev_sol[IndexEnum.discharge] < 1e-5:
206
+ equation_object.coefficients = np.array(
207
+ [-2.0 * self.loss_coefficient * 1e-5, -1.0, 1.0]
208
+ )
209
+ equation_object.rhs = -self.loss_coefficient * self.prev_sol[IndexEnum.discharge] * 1e-5
210
+ else:
211
+ equation_object.coefficients = np.array(
212
+ [-2.0 * self.loss_coefficient * abs(self.prev_sol[IndexEnum.discharge]), -1.0, 1.0]
213
+ )
214
+ equation_object.rhs = (
215
+ -self.loss_coefficient
216
+ * self.prev_sol[IndexEnum.discharge]
217
+ * abs(self.prev_sol[IndexEnum.discharge])
218
+ )
219
+ return equation_object
220
+
221
+ def update_loss_coefficient(self) -> None:
222
+ """Basic function which does not do anything, but can be overwritten in derived classes."""
223
+
224
+ def update_heat_supplied(self) -> None:
225
+ """Basic function which does not do anything, but can be overwritten in derived classes."""
@@ -0,0 +1,274 @@
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 the node class."""
17
+ import uuid
18
+
19
+ import numpy as np
20
+
21
+ from simulator_core.solver.matrix.core_enum import NUMBER_CORE_QUANTITIES, IndexEnum
22
+ from simulator_core.solver.matrix.equation_object import EquationObject
23
+ from simulator_core.solver.network.assets.base_item import BaseItem
24
+ from simulator_core.solver.network.assets.base_node_item import BaseNodeItem
25
+ from simulator_core.solver.utils.fluid_properties import fluid_props
26
+
27
+
28
+ class Node(BaseNodeItem):
29
+ """
30
+ A class to represent a node in a network.
31
+
32
+ This class inherits from the BaseItem class and implements the methods to generate the
33
+ equations for the node.
34
+
35
+ Attributes
36
+ ----------
37
+ connected_assets : list[list[BaseItem, int]]
38
+ A list of lists that store the asset objects and the connection point indices that
39
+ are connected to the node.
40
+
41
+ Methods
42
+ -------
43
+ connect_asset(asset: BaseItem, connection_point: int)
44
+ Connects an asset object to the node at the given connection point index.
45
+ get_equations() -> list[EquationObject]
46
+ Returns a list of EquationObjects that represent the equations for the node.
47
+ add_energy_equations() -> EquationObject
48
+ Returns an EquationObject that represents the energy balance equation for the node.
49
+ add_node_cont_equation() -> EquationObject
50
+ Returns an EquationObject that represents the mass flow rate continuity equation for
51
+ the node.
52
+ add_discharge_equation() -> EquationObject
53
+ Returns an EquationObject that represents the discharge equation for the node.
54
+ add_pressure_set_equation() -> EquationObject
55
+ Returns an EquationObject that represents the pressure set equation for the node.
56
+ set_temperature_equation() -> EquationObject
57
+ Returns an EquationObject that represents the temperature set equation for the node.
58
+ add_energy_equation() -> EquationObject
59
+ Returns an EquationObject that represents the energy equation for the node.
60
+ is_connected() -> bool
61
+ Returns True if the node is connected to any asset, False otherwise.
62
+ """
63
+
64
+ def __init__(
65
+ self,
66
+ name: uuid.UUID,
67
+ number_of_unknowns: int = 3,
68
+ height: float = 0.0,
69
+ initial_temperature: float = 273.15,
70
+ set_pressure: float = 10000.0,
71
+ ):
72
+ """Initializes the Node object with the given parameters.
73
+
74
+ :param uuid.UUID name: The unique identifier of the node.
75
+ :param int, optional number_of_unknowns: The number of unknown variables for the node.
76
+ The default is 3.
77
+ """
78
+ super().__init__(number_of_unknowns, name)
79
+ self.connected_assets: list[tuple[BaseItem, int]] = []
80
+ self.height = height
81
+ self.initial_temperature = initial_temperature
82
+ self.set_pressure = set_pressure
83
+
84
+ def connect_asset(self, asset: BaseItem, connection_point: int) -> None:
85
+ """Connects an asset object at the given connection point to the node .
86
+
87
+ :param BaseAsset asset: The asset object that is connected to the node.
88
+ :param int connection_point: The connection point for the asset which is connected to this
89
+ node.
90
+ :return:
91
+ """
92
+ if connection_point > (asset.number_of_connection_point - 1):
93
+ raise ValueError(
94
+ f"Connection point {connection_point} does not exist on asset {asset.name}."
95
+ )
96
+ # Check if the asset is already connected to the node at the given connection point
97
+ if set((asset, connection_point)) in set(self.connected_assets):
98
+ raise ValueError(
99
+ f"Asset {asset.name} is already connected to node {self.name} at connection"
100
+ + f" point {connection_point}."
101
+ )
102
+ else:
103
+ # Connect the asset to the node
104
+ self.connected_assets.append((asset, connection_point))
105
+
106
+ def get_equations(self) -> list[EquationObject]:
107
+ """Returns a list of EquationObjects that represent the equations for the node.
108
+
109
+ The equations are:
110
+
111
+ - Mass flow rate continuity equation
112
+ - Energy balance equation
113
+ - Discharge equation
114
+ :return: list[EquationObject]
115
+ A list of EquationObjects that contain the indices, coefficients, and right-hand side
116
+ values of the equations.
117
+ """
118
+ # Check connection
119
+ if not self.is_connected():
120
+ raise ValueError(f"Node {self.name} is not connected to any asset.")
121
+ # Construct equation object
122
+ equations = [
123
+ self.add_node_cont_equation(),
124
+ self.add_energy_equations(),
125
+ self.add_discharge_equation(),
126
+ ]
127
+ return equations
128
+
129
+ def add_energy_equations(self) -> EquationObject:
130
+ """Returns an EquationObject that represents the energy balance equation for the node.
131
+
132
+ When the mass flow rate of all connected components is smaller or equal 0.
133
+ Then the node will pre-scribe its temperature otherwise it will give
134
+ an equation where the sum of mass flow rat times specific internal energy is zero.
135
+
136
+
137
+ :return: EquationObject An EquationObject that contains the indices, coefficients,
138
+ and right-hand side value of the equation.
139
+ """
140
+ flows = np.array(
141
+ [
142
+ asset.prev_sol[
143
+ IndexEnum.discharge + asset_connection_point * NUMBER_CORE_QUANTITIES
144
+ ]
145
+ for asset, asset_connection_point in self.connected_assets
146
+ ]
147
+ )
148
+
149
+ if all(flows >= 0.0) or all(flows <= 0.0):
150
+ return self.set_temperature_equation()
151
+ else:
152
+ return self.add_energy_equation()
153
+
154
+ def add_node_cont_equation(self) -> EquationObject:
155
+ """Returns an EquationObject that represents the mass continuity equation for the node.
156
+
157
+ :return: EquationObject
158
+ An EquationObject that contains the indices, coefficients, and right-hand side value
159
+ of the equation.
160
+ """
161
+ equation_object = EquationObject()
162
+ equation_object.indices = np.array([self.matrix_index + IndexEnum.discharge])
163
+ equation_object.coefficients = np.array([1.0])
164
+ equation_object.rhs = 0.0
165
+ for asset, asset_connection_point in self.connected_assets:
166
+ equation_object.indices = np.append(
167
+ equation_object.indices,
168
+ [
169
+ asset.matrix_index
170
+ + IndexEnum.discharge
171
+ + asset_connection_point * NUMBER_CORE_QUANTITIES
172
+ ],
173
+ )
174
+ equation_object.coefficients = np.append(equation_object.coefficients, [1.0])
175
+ return equation_object
176
+
177
+ def add_discharge_equation(self) -> EquationObject:
178
+ """Returns an EquationObject that represents the discharge is zero equation for the node.
179
+
180
+ :return: EquationObject
181
+ An EquationObject that contains the indices, coefficients, and right-hand side
182
+ value of the equation.
183
+ """
184
+ equation_object = EquationObject()
185
+ equation_object.indices = np.array([self.matrix_index + IndexEnum.discharge])
186
+ equation_object.coefficients = np.array([1.0])
187
+ equation_object.rhs = 0.0
188
+ return equation_object
189
+
190
+ def add_pressure_set_equation(self) -> EquationObject:
191
+ """Returns an EquationObject that sets the pressure of the node to a pre-defined value.
192
+
193
+ :return: EquationObject
194
+ An EquationObject that contains the indices, coefficients, and right-hand side
195
+ value of the equation.
196
+ """
197
+ equation_object = EquationObject()
198
+ equation_object.indices = np.array([self.matrix_index + IndexEnum.pressure])
199
+ equation_object.coefficients = np.array([1.0])
200
+ equation_object.rhs = self.set_pressure
201
+ return equation_object
202
+
203
+ def set_temperature_equation(self) -> EquationObject:
204
+ """Returns an EquationObject that sets the temperature of the node to a pre-defined value.
205
+
206
+ :return: EquationObject
207
+ An EquationObject that contains the indices, coefficients, and right-hand side
208
+ value of the equation.
209
+ """
210
+ equation_object = EquationObject()
211
+ equation_object.indices = np.array([self.matrix_index + IndexEnum.internal_energy])
212
+ equation_object.coefficients = np.array([1.0])
213
+ equation_object.rhs = fluid_props.get_ie(self.initial_temperature)
214
+ return equation_object
215
+
216
+ def add_energy_equation(self) -> EquationObject:
217
+ """Returns an EquationObject that represents the energy equation for the node.
218
+
219
+ :return: EquationObject
220
+ An EquationObject that contains the indices, coefficients, and right-hand side
221
+ value of the equation
222
+ """
223
+ equation_object = EquationObject()
224
+ equation_object.indices = np.array(
225
+ [self.matrix_index + IndexEnum.discharge, self.matrix_index + IndexEnum.internal_energy]
226
+ )
227
+ # Be aware that the coefficients are in reverse order
228
+ equation_object.coefficients = np.array(self.prev_sol)[
229
+ (equation_object.indices - self.matrix_index)[::-1]
230
+ ]
231
+ equation_object.rhs = np.prod(equation_object.coefficients)
232
+ # Extend the equation_object with the indices and coefficients of the connected assets
233
+ for asset, asset_connection_id in self.connected_assets:
234
+ # Extended asset indices
235
+ extra_indices = np.array(
236
+ [
237
+ asset.matrix_index
238
+ + IndexEnum.discharge
239
+ + asset_connection_id * NUMBER_CORE_QUANTITIES,
240
+ asset.matrix_index
241
+ + IndexEnum.internal_energy
242
+ + asset_connection_id * NUMBER_CORE_QUANTITIES,
243
+ ]
244
+ )
245
+ # Extend the indices and coefficients of the equation object
246
+ equation_object.indices = np.append(
247
+ equation_object.indices,
248
+ extra_indices,
249
+ )
250
+ # Extend the coefficients of the equation object
251
+ prev_sol = np.array(asset.prev_sol)
252
+ equation_object.coefficients = np.append(
253
+ equation_object.coefficients,
254
+ prev_sol[(extra_indices - asset.matrix_index)[::-1]],
255
+ )
256
+ # Extend the right-hand side of the equation object
257
+ equation_object.rhs += np.prod(prev_sol[extra_indices - asset.matrix_index])
258
+ return equation_object
259
+
260
+ def is_connected(self) -> bool:
261
+ """Returns True if the node is connected to any asset, False otherwise.
262
+
263
+ :return: bool
264
+ A boolean value that indicates whether the node is connected or not.
265
+ """
266
+ return len(self.connected_assets) > 0
267
+
268
+ def get_connected_assets(self) -> list[tuple[BaseItem, int]]:
269
+ """Returns the connected assets of the node.
270
+
271
+ :return: Tuple[BaseItem, int]
272
+ A tuple of the connected asset and the connection point index.
273
+ """
274
+ return self.connected_assets