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.
- omotes_simulator_core-0.0.3.dev0.dist-info/LICENSE +621 -0
- omotes_simulator_core-0.0.3.dev0.dist-info/METADATA +55 -0
- omotes_simulator_core-0.0.3.dev0.dist-info/RECORD +59 -0
- omotes_simulator_core-0.0.3.dev0.dist-info/WHEEL +5 -0
- omotes_simulator_core-0.0.3.dev0.dist-info/top_level.txt +1 -0
- simulator_core/__init__.py +15 -0
- simulator_core/__main__.py +21 -0
- simulator_core/adapter/__init__.py +16 -0
- simulator_core/adapter/presentation/__init__.py +16 -0
- simulator_core/adapter/transforms/__init__.py +16 -0
- simulator_core/adapter/transforms/esdl_asset_mapper.py +53 -0
- simulator_core/adapter/transforms/mappers.py +226 -0
- simulator_core/adapter/transforms/string_to_esdl.py +66 -0
- simulator_core/adapter/transforms/transform_utils.py +34 -0
- simulator_core/entities/__init__.py +16 -0
- simulator_core/entities/assets/__init__.py +16 -0
- simulator_core/entities/assets/asset_abstract.py +116 -0
- simulator_core/entities/assets/asset_defaults.py +68 -0
- simulator_core/entities/assets/controller_classes.py +81 -0
- simulator_core/entities/assets/demand_cluster.py +117 -0
- simulator_core/entities/assets/esdl_asset_object.py +60 -0
- simulator_core/entities/assets/junction.py +64 -0
- simulator_core/entities/assets/pipe.py +174 -0
- simulator_core/entities/assets/production_cluster.py +222 -0
- simulator_core/entities/assets/utils.py +154 -0
- simulator_core/entities/esdl_object.py +90 -0
- simulator_core/entities/heat_network.py +82 -0
- simulator_core/entities/network_controller.py +67 -0
- simulator_core/entities/simulation_configuration.py +31 -0
- simulator_core/entities/utility/__init__.py +16 -0
- simulator_core/entities/utility/influxdb_reader.py +129 -0
- simulator_core/infrastructure/__init__.py +16 -0
- simulator_core/infrastructure/app.py +55 -0
- simulator_core/infrastructure/simulation_manager.py +55 -0
- simulator_core/infrastructure/utils.py +45 -0
- simulator_core/py.typed +0 -0
- simulator_core/simulation/__init__.py +18 -0
- simulator_core/simulation/mappers/__init__.py +16 -0
- simulator_core/simulation/mappers/mappers.py +35 -0
- simulator_core/simulation/networksimulation.py +63 -0
- simulator_core/solver/__init__.py +15 -0
- simulator_core/solver/matrix/__init__.py +15 -0
- simulator_core/solver/matrix/core_enum.py +26 -0
- simulator_core/solver/matrix/equation_object.py +56 -0
- simulator_core/solver/matrix/matrix.py +125 -0
- simulator_core/solver/matrix/utility.py +38 -0
- simulator_core/solver/network/__init__.py +15 -0
- simulator_core/solver/network/assets/__init__.py +15 -0
- simulator_core/solver/network/assets/base_asset.py +303 -0
- simulator_core/solver/network/assets/base_item.py +63 -0
- simulator_core/solver/network/assets/base_node_item.py +67 -0
- simulator_core/solver/network/assets/boundary.py +83 -0
- simulator_core/solver/network/assets/fall_type.py +225 -0
- simulator_core/solver/network/assets/node.py +274 -0
- simulator_core/solver/network/assets/production_asset.py +171 -0
- simulator_core/solver/network/assets/solver_pipe.py +541 -0
- simulator_core/solver/network/network.py +398 -0
- simulator_core/solver/solver.py +80 -0
- 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
|