exerpy 0.0.1__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 (44) hide show
  1. exerpy/__init__.py +12 -0
  2. exerpy/analyses.py +1711 -0
  3. exerpy/components/__init__.py +16 -0
  4. exerpy/components/combustion/__init__.py +0 -0
  5. exerpy/components/combustion/base.py +248 -0
  6. exerpy/components/component.py +126 -0
  7. exerpy/components/heat_exchanger/__init__.py +0 -0
  8. exerpy/components/heat_exchanger/base.py +449 -0
  9. exerpy/components/heat_exchanger/condenser.py +323 -0
  10. exerpy/components/heat_exchanger/simple.py +358 -0
  11. exerpy/components/heat_exchanger/steam_generator.py +264 -0
  12. exerpy/components/helpers/__init__.py +0 -0
  13. exerpy/components/helpers/cycle_closer.py +104 -0
  14. exerpy/components/nodes/__init__.py +0 -0
  15. exerpy/components/nodes/deaerator.py +318 -0
  16. exerpy/components/nodes/drum.py +164 -0
  17. exerpy/components/nodes/flash_tank.py +89 -0
  18. exerpy/components/nodes/mixer.py +332 -0
  19. exerpy/components/piping/__init__.py +0 -0
  20. exerpy/components/piping/valve.py +394 -0
  21. exerpy/components/power_machines/__init__.py +0 -0
  22. exerpy/components/power_machines/generator.py +168 -0
  23. exerpy/components/power_machines/motor.py +173 -0
  24. exerpy/components/turbomachinery/__init__.py +0 -0
  25. exerpy/components/turbomachinery/compressor.py +318 -0
  26. exerpy/components/turbomachinery/pump.py +310 -0
  27. exerpy/components/turbomachinery/turbine.py +351 -0
  28. exerpy/data/Ahrendts.json +90 -0
  29. exerpy/functions.py +637 -0
  30. exerpy/parser/__init__.py +0 -0
  31. exerpy/parser/from_aspen/__init__.py +0 -0
  32. exerpy/parser/from_aspen/aspen_config.py +61 -0
  33. exerpy/parser/from_aspen/aspen_parser.py +721 -0
  34. exerpy/parser/from_ebsilon/__init__.py +38 -0
  35. exerpy/parser/from_ebsilon/check_ebs_path.py +74 -0
  36. exerpy/parser/from_ebsilon/ebsilon_config.py +1055 -0
  37. exerpy/parser/from_ebsilon/ebsilon_functions.py +181 -0
  38. exerpy/parser/from_ebsilon/ebsilon_parser.py +660 -0
  39. exerpy/parser/from_ebsilon/utils.py +79 -0
  40. exerpy/parser/from_tespy/tespy_config.py +23 -0
  41. exerpy-0.0.1.dist-info/METADATA +158 -0
  42. exerpy-0.0.1.dist-info/RECORD +44 -0
  43. exerpy-0.0.1.dist-info/WHEEL +4 -0
  44. exerpy-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,264 @@
1
+ import logging
2
+
3
+ from exerpy.components.component import Component
4
+ from exerpy.components.component import component_registry
5
+
6
+
7
+ @component_registry
8
+ class SteamGenerator(Component):
9
+ r"""
10
+ Class for exergy analysis of steam generators.
11
+
12
+ This class performs exergy analysis calculations for steam generators.
13
+ The component has several input and output streams as follows:
14
+
15
+ Inlet streams:
16
+ - inl[0]: Feed water inlet (high pressure)
17
+ - inl[1]: Steam inlet (intermediate pressure)
18
+ - inl[2]: Heat inlet (providing the heat input Q)
19
+ - inl[3]: Water injection (high pressure)
20
+ - inl[4]: Water injection (intermediate pressure)
21
+
22
+ Outlet streams:
23
+ - outl[0]: Superheated steam outlet (high pressure)
24
+ - outl[1]: Superheated steam outlet (intermediate pressure)
25
+ - outl[2]: Drain / Blow down outlet
26
+
27
+ The exergy product is defined as:
28
+
29
+ .. math::
30
+ \dot{E}_P = \Bigl[ \dot{m}_{\mathrm{out,HP}}\,e_{\mathrm{out,HP}}
31
+ - \dot{m}_{\mathrm{in,HP}}\,e_{\mathrm{in,HP}} \Bigr]
32
+ + \Bigl[ \dot{m}_{\mathrm{out,IP}}\,e_{\mathrm{out,IP}}
33
+ - \dot{m}_{\mathrm{in,IP}}\,e_{\mathrm{in,IP}} \Bigr]
34
+ - \dot{m}_{\mathrm{w,HP}}\,e_{\mathrm{w,HP}}
35
+ - \dot{m}_{\mathrm{w,IP}}\,e_{\mathrm{w,IP}}
36
+
37
+ where:
38
+ - \(\dot{m}\) is the mass flow rate and \(e\) is the specific exergy of the stream.
39
+ - The subscripts HP and IP denote high and intermediate pressure streams,
40
+ respectively, and 'w' stands for water injection.
41
+
42
+ The exergy fuel is computed from the heat input as:
43
+
44
+ .. math::
45
+ \dot{E}_F = E_q = Q \left( 1 - \frac{T_b}{T_0} \right)
46
+
47
+ with the thermodynamic temperature difference defined by
48
+
49
+ .. math::
50
+ T_b = \frac{h_{\mathrm{out,HP}} - h_{\mathrm{in,HP}}}{s_{\mathrm{out,HP}} - s_{\mathrm{in,HP}}}
51
+
52
+ where:
53
+ - \(h\) and \(s\) are the specific enthalpy and entropy,
54
+ - \(T_0\) is the ambient temperature.
55
+
56
+ The exergy destruction and efficiency are then given by:
57
+
58
+ .. math::
59
+ \dot{E}_D = \dot{E}_F - \dot{E}_P \quad\mathrm{and}\quad \varepsilon = \frac{\dot{E}_P}{\dot{E}_F}
60
+
61
+ Parameters
62
+ ----------
63
+ **kwargs : dict
64
+ Arbitrary keyword arguments passed to the parent class.
65
+
66
+ Attributes
67
+ ----------
68
+ E_F : float
69
+ Exergy fuel of the component :math:`\dot{E}_F` in :math:`\mathrm{W}`.
70
+ E_P : float
71
+ Exergy product of the component :math:`\dot{E}_P` in :math:`\mathrm{W}`.
72
+ E_D : float
73
+ Exergy destruction of the component :math:`\dot{E}_D` in :math:`\mathrm{W}`.
74
+ epsilon : float
75
+ Exergetic efficiency :math:`\varepsilon`.
76
+ inl : dict
77
+ Dictionary containing inlet streams.
78
+ outl : dict
79
+ Dictionary containing outlet streams.
80
+ """
81
+
82
+ def __init__(self, **kwargs):
83
+ r"""Initialize steam generator component with given parameters."""
84
+ self.dissipative = False
85
+ super().__init__(**kwargs)
86
+
87
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
88
+ r"""
89
+ Calculate the exergy balance of the steam generator.
90
+
91
+ This method computes the exergy fuel from the heat inlet using the relation
92
+
93
+ .. math::
94
+ E_F = Q \left(1 - \frac{T_b}{T_0}\right),
95
+ \quad T_b = \frac{h_{\mathrm{out,HP}} - h_{\mathrm{in,HP}}}{s_{\mathrm{out,HP}} - s_{\mathrm{in,HP}}}
96
+
97
+ and the exergy product as
98
+
99
+ .. math::
100
+ E_P = \Bigl[ \dot{m}_{\mathrm{out,HP}}\,e_{\mathrm{out,HP}} -
101
+ \dot{m}_{\mathrm{in,HP}}\,e_{\mathrm{in,HP}} \Bigr]
102
+ + \Bigl[ \dot{m}_{\mathrm{out,IP}}\,e_{\mathrm{out,IP}} -
103
+ \dot{m}_{\mathrm{in,IP}}\,e_{\mathrm{in,IP}} \Bigr]
104
+ - \dot{m}_{\mathrm{w,HP}}\,e_{\mathrm{w,HP}}
105
+ - \dot{m}_{\mathrm{w,IP}}\,e_{\mathrm{w,IP}}
106
+
107
+ The exergy destruction is given by
108
+
109
+ .. math::
110
+ E_D = E_F - E_P
111
+
112
+ and the exergetic efficiency is
113
+
114
+ .. math::
115
+ \varepsilon = \frac{E_P}{E_F}
116
+
117
+ Parameters
118
+ ----------
119
+ T0 : float
120
+ Ambient temperature in Kelvin.
121
+ """
122
+ # Ensure that all necessary streams exist
123
+ required_inlets = [0]
124
+ required_outlets = [0, 2]
125
+ for idx in required_inlets:
126
+ if idx not in self.inl:
127
+ raise ValueError(f"Missing inlet stream with index {idx}.")
128
+ for idx in required_outlets:
129
+ if idx not in self.outl:
130
+ raise ValueError(f"Missing outlet stream with index {idx}.")
131
+
132
+ # Calculate T_b for high pressure streams: from superheated steam outlet (HP) and feed water inlet (HP)
133
+ try:
134
+ T_b = (self.outl[0]['h'] - self.inl[0]['h']) / (self.outl[0]['s'] - self.inl[0]['s'])
135
+ except ZeroDivisionError:
136
+ raise ZeroDivisionError("Division by zero encountered in calculating T_b. Check entropy differences.")
137
+
138
+ if split_physical_exergy:
139
+ exergy_type = 'e_T'
140
+ else:
141
+ exergy_type = 'e_PH'
142
+
143
+ # Calculate exergy fuel
144
+ # High pressure part: Superheated steam outlet (HP) minus Feed water inlet (HP)
145
+ E_F_HP = self.outl[0]['m'] * self.outl[0][exergy_type] - self.inl[0]['m'] * self.inl[0][exergy_type]
146
+ # Intermediate pressure part: Superheated steam outlet (IP) minus Steam inlet (IP)
147
+ E_F_IP = self.outl.get(1, {}).get('m', 0) * self.outl.get(1, {}).get(exergy_type, 0) - self.inl.get(1, {}).get('m', 0) * self.inl.get(1, {}).get(exergy_type, 0)
148
+ # Water injection contributions (assumed to be negative)
149
+ E_F_w_inj = self.inl.get(2, {}).get('m', 0) * self.inl.get(2, {}).get(exergy_type, 0) + self.inl.get(3, {}).get('m', 0) * self.inl.get(3, {}).get(exergy_type, 0)
150
+ self.E_F = E_F_HP + E_F_IP - E_F_w_inj
151
+ logging.warning(f"Since the temperature level of the heat source of the steam generator is unknown, "
152
+ "the exergy fuel of this component is calculated based on the thermal exergy value of the water streams.")
153
+ # Calculate exergy product
154
+ # High pressure part: Superheated steam outlet (HP) minus Feed water inlet (HP)
155
+ E_P_HP = self.outl[0]['m'] * self.outl[0]['e_PH'] - self.inl[0]['m'] * self.inl[0]['e_PH']
156
+ # Intermediate pressure part: Superheated steam outlet (IP) minus Steam inlet (IP)
157
+ E_P_IP = self.outl.get(1, {}).get('m', 0) * self.outl.get(1, {}).get('e_PH', 0) - self.inl.get(1, {}).get('m', 0) * self.inl.get(1, {}).get('e_PH', 0)
158
+ # Water injection contributions (assumed to be negative)
159
+ E_P_w_inj = self.inl.get(2, {}).get('m', 0) * self.inl.get(2, {}).get('e_PH', 0) + self.inl.get(3, {}).get('m', 0) * self.inl.get(3, {}).get('e_PH', 0)
160
+ self.E_P = E_P_HP + E_P_IP - E_P_w_inj
161
+
162
+ # Calculate exergy destruction and efficiency
163
+ self.E_D = self.E_F - self.E_P
164
+ self.epsilon = self.calc_epsilon()
165
+
166
+ # Log the results
167
+ logging.info(
168
+ f"SteamGenerator exergy balance calculated: "
169
+ f"E_P = {self.E_P:.2f} W, E_F = {self.E_F:.2f} W, "
170
+ f"E_D = {self.E_D:.2f} W, Efficiency = {self.epsilon:.2%}"
171
+ )
172
+
173
+
174
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
175
+ """
176
+ Auxiliary equations for the steam generator.
177
+
178
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
179
+ the following auxiliary cost relations:
180
+
181
+ (1) c_T(heat_source)/E_F = c_T(HP_outlet)/E_T(HP) + c_T(IP_outlet)/E_T(IP)
182
+ - P-principle: thermal exergy costs from heat source are distributed to steam outlets
183
+
184
+ (2) 1/E_M_in(HP) * C_M_in(HP) - 1/E_M_out(HP) * C_M_out(HP) = 0
185
+ - F-principle: specific mechanical exergy costs equalized between HP inlet/outlet
186
+
187
+ (3) 1/E_M_in(IP) * C_M_in(IP) - 1/E_M_out(IP) * C_M_out(IP) = 0
188
+ - F-principle: specific mechanical exergy costs equalized between IP inlet/outlet
189
+
190
+ (4-5) Chemical exergy cost equations (if enabled) for HP and IP streams
191
+ - F-principle: specific chemical exergy costs equalized between inlets/outlets
192
+
193
+ Parameters
194
+ ----------
195
+ A : numpy.ndarray
196
+ The current cost matrix.
197
+ b : numpy.ndarray
198
+ The current right-hand-side vector.
199
+ counter : int
200
+ The current row index in the matrix.
201
+ T0 : float
202
+ Ambient temperature.
203
+ equations : dict
204
+ Dictionary for storing equation labels.
205
+ chemical_exergy_enabled : bool
206
+ Flag indicating whether chemical exergy auxiliary equations should be added.
207
+
208
+ Returns
209
+ -------
210
+ A : numpy.ndarray
211
+ The updated cost matrix.
212
+ b : numpy.ndarray
213
+ The updated right-hand-side vector.
214
+ counter : int
215
+ The updated row index.
216
+ equations : dict
217
+ Updated dictionary with equation labels.
218
+ """
219
+
220
+ def exergoeconomic_balance(self, T0):
221
+ """
222
+ Perform exergoeconomic balance calculations for the steam generator.
223
+
224
+ This method calculates various exergoeconomic parameters including:
225
+ - Cost rates of product (C_P) and fuel (C_F)
226
+ - Specific cost of product (c_P) and fuel (c_F)
227
+ - Cost rate of exergy destruction (C_D)
228
+ - Relative cost difference (r)
229
+ - Exergoeconomic factor (f)
230
+
231
+ Parameters
232
+ ----------
233
+ T0 : float
234
+ Ambient temperature
235
+
236
+ Notes
237
+ -----
238
+ The exergoeconomic balance considers thermal (T), chemical (CH),
239
+ and mechanical (M) exergy components for the inlet and outlet streams.
240
+ """
241
+ # 1) Product cost rate: HP and IP steam net physical exergy costs, minus injection
242
+ C_P_hp = (self.outl[0]['m'] * self.outl[0]['C_PH']
243
+ - self.inl[0]['m'] * self.inl[0]['C_PH'])
244
+ C_P_ip = 0.0
245
+ if 1 in self.outl and 1 in self.inl:
246
+ C_P_ip = (self.outl[1]['m'] * self.outl[1]['C_PH']
247
+ - self.inl[1]['m'] * self.inl[1]['C_PH'])
248
+ # Subtract water injection costs
249
+ C_P_w = 0.0
250
+ if 3 in self.inl:
251
+ C_P_w += self.inl[3]['m'] * self.inl[3]['C_PH']
252
+ if 4 in self.inl:
253
+ C_P_w += self.inl[4]['m'] * self.inl[4]['C_PH']
254
+ self.C_P = C_P_hp + C_P_ip - C_P_w
255
+
256
+ # 2) Fuel cost rate: cost of heat exergy stream
257
+ self.C_F = self.inl[2]['C_T']
258
+
259
+ # 3) Specific costs and destruction cost
260
+ self.c_F = self.C_F / self.E_F if self.E_F != 0 else float('nan')
261
+ self.c_P = self.C_P / self.E_P if self.E_P != 0 else float('nan')
262
+ self.C_D = self.C_F - self.C_P
263
+ self.r = (self.c_P - self.c_F) / self.c_F if self.c_F != 0 else float('nan')
264
+ self.f = self.Z_costs / (self.Z_costs + self.C_D) if (self.Z_costs + self.C_D) != 0 else float('nan')
File without changes
@@ -0,0 +1,104 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from exerpy.components.component import Component
6
+ from exerpy.components.component import component_registry
7
+
8
+
9
+ @component_registry
10
+ class CycleCloser(Component):
11
+ r"""
12
+ Component for closing cycles. This component is not analyzed in exergy analysis.
13
+ """
14
+ def __init__(self, **kwargs):
15
+ r"""Initialize CycleCloser component with given parameters."""
16
+ super().__init__(**kwargs)
17
+
18
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
19
+ r"""
20
+ The CycleCloser component does not have an exergy balance calculation.
21
+ """
22
+ self.E_D = np.nan
23
+ self.E_F = np.nan
24
+ self.E_P = np.nan
25
+ self.E_L = np.nan
26
+ self.epsilon = np.nan
27
+
28
+ # Log the results
29
+ logging.info(
30
+ f"The exergy balance of a CycleCloser component is skipped."
31
+ )
32
+
33
+
34
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
35
+ """
36
+ Auxiliary equations for the cycle closer.
37
+
38
+ This function adds two rows to the cost matrix A and the right-hand side vector b to enforce
39
+ the following auxiliary cost relations:
40
+
41
+ (1) 1/E_M_in * C_M_in - 1/E_M_out * C_M_out = 0
42
+ (2) 1/E_T_in * C_T_in - 1/E_T_out * C_T_out = 0
43
+
44
+ These equations ensure that the specific mechanical and thermal costs are equalized between
45
+ the inlet and outlet of the cycle closer. Chemical exergy is not considered for the cycle closer.
46
+
47
+ Parameters
48
+ ----------
49
+ A : numpy.ndarray
50
+ The current cost matrix.
51
+ b : numpy.ndarray
52
+ The current right-hand-side vector.
53
+ counter : int
54
+ The current row index in the matrix.
55
+ T0 : float
56
+ Ambient temperature (not used in this component).
57
+ equations : list or dict
58
+ Data structure for storing equation labels.
59
+ chemical_exergy_enabled : bool
60
+ Flag indicating whether chemical exergy auxiliary equations should be added.
61
+ This flag is ignored for CycleCloser.
62
+
63
+ Returns
64
+ -------
65
+ A : numpy.ndarray
66
+ The updated cost matrix.
67
+ b : numpy.ndarray
68
+ The updated right-hand-side vector.
69
+ counter : int
70
+ The updated row index (increased by 2).
71
+ equations : list or dict
72
+ Updated structure with equation labels.
73
+ """
74
+ # Mechanical cost equality equation:
75
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = (1 / self.inl[0]["e_M"]) if self.inl[0]["e_M"] != 0 else 1
76
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = (-1 / self.outl[0]["e_M"]) if self.outl[0]["e_M"] != 0 else -1
77
+ equations[counter] = f"aux_cyclecloser_mech"
78
+ b[counter] = 0
79
+
80
+ # Thermal cost equality equation:
81
+ A[counter+1, self.inl[0]["CostVar_index"]["T"]] = (1 / self.inl[0]["e_T"]) if self.inl[0]["e_T"] != 0 else 1
82
+ A[counter+1, self.outl[0]["CostVar_index"]["T"]] = (-1 / self.outl[0]["e_T"]) if self.outl[0]["e_T"] != 0 else -1
83
+ equations[counter+1] = f"aux_cyclecloser_thermal"
84
+ b[counter+1] = 0
85
+
86
+ counter += 2
87
+ return A, b, counter, equations
88
+
89
+ def exergoeconomic_balance(self, T0):
90
+ """
91
+ Placeholder for exergoeconomic balance calculations.
92
+
93
+ The CycleCloser component is not considered in exergoeconomic analysis
94
+ and all calculations are skipped. NaN values are assigned to all
95
+ exergoeconomic parameters.
96
+ """
97
+
98
+ self.C_P = np.nan
99
+ self.C_F = np.nan
100
+ self.c_F = np.nan
101
+ self.c_P = np.nan
102
+ self.C_D = np.nan
103
+ self.r = np.nan
104
+ self.f = np.nan
File without changes
@@ -0,0 +1,318 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from exerpy.components.component import Component
6
+ from exerpy.components.component import component_registry
7
+
8
+
9
+ @component_registry
10
+ class Deaerator(Component):
11
+ r"""
12
+ Class for exergy analysis of deaerators.
13
+
14
+ This class performs exergy analysis calculations for deaerators with multiple
15
+ inlet streams and one outlet stream. The exergy product and fuel definitions
16
+ vary based on the temperature relationships between inlet streams, outlet
17
+ stream, and ambient conditions.
18
+
19
+ Parameters
20
+ ----------
21
+ **kwargs : dict
22
+ Arbitrary keyword arguments passed to parent class.
23
+
24
+ Attributes
25
+ ----------
26
+ E_F : float
27
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
28
+ E_P : float
29
+ Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
30
+ E_D : float
31
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
32
+ epsilon : float
33
+ Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
34
+ inl : dict
35
+ Dictionary containing inlet streams data with temperature, mass flows,
36
+ and specific exergies.
37
+ outl : dict
38
+ Dictionary containing outlet stream data with temperature, mass flows,
39
+ and specific exergies.
40
+
41
+ Notes
42
+ -----
43
+ The exergy analysis accounts for physical exergy only. The equations for exergy
44
+ product and fuel are defined based on temperature relationships:
45
+
46
+ .. math::
47
+
48
+ \dot{E}_\mathrm{P} =
49
+ \begin{cases}
50
+ \begin{cases}
51
+ \sum_i \dot{m}_i \cdot (e_\mathrm{out}^\mathrm{PH} -
52
+ e_{\mathrm{in,}i}^\mathrm{PH})
53
+ & T_{\mathrm{in,}i} < T_\mathrm{out} \mathrm{ & }
54
+ T_{\mathrm{in,}i} \geq T_0 \\
55
+ \sum_i \dot{m}_i \cdot e_\mathrm{out}^\mathrm{PH}
56
+ & T_{\mathrm{in,}i} < T_\mathrm{out} \mathrm{ & }
57
+ T_{\mathrm{in,}i} < T_0 \\
58
+ \end{cases} & T_\mathrm{out} > T_0\\
59
+ \mathrm{not defined (nan)} & T_\mathrm{out} = T_0\\
60
+ \begin{cases}
61
+ \sum_i \dot{m}_i \cdot e_\mathrm{out}^\mathrm{PH}
62
+ & T_{\mathrm{in,}i} > T_\mathrm{out} \mathrm{ & }
63
+ T_{\mathrm{in,}i} \geq T_0 \\
64
+ \sum_i \dot{m}_i \cdot (e_\mathrm{out}^\mathrm{PH} -
65
+ e_{\mathrm{in,}i}^\mathrm{PH})
66
+ & T_{\mathrm{in,}i} > T_\mathrm{out} \mathrm{ & }
67
+ T_{\mathrm{in,}i} < T_0 \\
68
+ \end{cases} & T_\mathrm{out} < T_0\\
69
+ \end{cases}
70
+
71
+ \dot{E}_\mathrm{F} =
72
+ \begin{cases}
73
+ \begin{cases}
74
+ \sum_i \dot{m}_i \cdot (e_{\mathrm{in,}i}^\mathrm{PH} -
75
+ e_\mathrm{out}^\mathrm{PH})
76
+ & T_{\mathrm{in,}i} > T_\mathrm{out} \\
77
+ \sum_i \dot{m}_i \cdot e_{\mathrm{in,}i}^\mathrm{PH}
78
+ & T_{\mathrm{in,}i} < T_\mathrm{out} \mathrm{ & }
79
+ T_{\mathrm{in,}i} < T_0 \\
80
+ \end{cases} & T_\mathrm{out} > T_0\\
81
+ \sum_i \dot{m}_i \cdot e_{\mathrm{in,}i}^\mathrm{PH}
82
+ & T_\mathrm{out} = T_0\\
83
+ \begin{cases}
84
+ \sum_i \dot{m}_i \cdot e_{\mathrm{in,}i}^\mathrm{PH}
85
+ & T_{\mathrm{in,}i} > T_\mathrm{out} \mathrm{ & }
86
+ T_{\mathrm{in,}i} \geq T_0 \\
87
+ \sum_i \dot{m}_i \cdot (e_{\mathrm{in,}i}^\mathrm{PH} -
88
+ e_\mathrm{out}^\mathrm{PH})
89
+ & T_{\mathrm{in,}i} < T_\mathrm{out} \\
90
+ \end{cases} & T_\mathrm{out} < T_0\\
91
+ \end{cases}
92
+
93
+ \forall i \in \mathrm{deaerator inlets}
94
+ """
95
+
96
+ def __init__(self, **kwargs):
97
+ r"""Initialize deaerator component with given parameters."""
98
+ super().__init__(**kwargs)
99
+
100
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
101
+ r"""
102
+ Calculate the exergy balance of the deaerator.
103
+
104
+ Performs exergy balance calculations considering the temperature relationships
105
+ between inlet streams, outlet stream, and ambient conditions.
106
+
107
+ Parameters
108
+ ----------
109
+ T0 : float
110
+ Ambient temperature in :math:`\mathrm{K}`.
111
+ p0 : float
112
+ Ambient pressure in :math:`\mathrm{Pa}`.
113
+ split_physical_exergy : bool
114
+ Flag indicating whether physical exergy is split into thermal and mechanical components.
115
+
116
+ Raises
117
+ ------
118
+ ValueError
119
+ If the required inlet and outlet streams are not properly defined.
120
+ """
121
+ # Ensure that the component has both inlet and outlet streams
122
+ if len(self.inl) < 2 or len(self.outl) < 1:
123
+ raise ValueError("Deaerator requires at least two inlets and one outlet.")
124
+
125
+ self.E_P = 0
126
+ self.E_F = 0
127
+
128
+ # Case 1: Outlet temperature is greater than T0
129
+ if self.outl[0]['T'] > T0:
130
+ for _, inlet in self.inl.items():
131
+ if inlet['T'] < self.outl[0]['T']: # Tin < Tout
132
+ if inlet['T'] >= T0: # and Tin >= T0
133
+ self.E_P += inlet['m'] * (self.outl[0]['e_PH'] - inlet['e_PH'])
134
+ else: # and Tin < T0
135
+ self.E_P += inlet['m'] * self.outl[0]['e_PH']
136
+ self.E_F += inlet['m'] * inlet['e_PH']
137
+ else: # Tin > Tout
138
+ self.E_F += inlet['m'] * (inlet['e_PH'] - self.outl[0]['e_PH'])
139
+
140
+ # Case 2: Outlet temperature is equal to T0
141
+ elif self.outl[0]['T'] == T0:
142
+ self.E_P = np.nan
143
+ for _, inlet in self.inl.items():
144
+ self.E_F += inlet['m'] * inlet['e_PH']
145
+
146
+ # Case 3: Outlet temperature is less than T0
147
+ else:
148
+ for _, inlet in self.inl.items():
149
+ if inlet['T'] > self.outl[0]['T']: # Tin > Tout
150
+ if inlet['T'] >= T0: # and Tin >= T0
151
+ self.E_P += inlet['m'] * self.outl[0]['e_PH']
152
+ self.E_F += inlet['m'] * inlet['e_PH']
153
+ else: # and Tin < T0
154
+ self.E_P += inlet['m'] * (self.outl[0]['e_PH'] - inlet['e_PH'])
155
+ else: # Tin < Tout
156
+ self.E_F += inlet['m'] * (inlet['e_PH'] - self.outl[0]['e_PH'])
157
+
158
+ # Calculate exergy destruction and efficiency
159
+ if np.isnan(self.E_P):
160
+ self.E_D = self.E_F
161
+ else:
162
+ self.E_D = self.E_F - self.E_P
163
+ self.epsilon = self.calc_epsilon()
164
+
165
+ # Log the results
166
+ logging.info(
167
+ f"Deaerator exergy balance calculated: "
168
+ f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
169
+ f"Efficiency={self.epsilon:.2%}"
170
+ )
171
+
172
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
173
+ """
174
+ Auxiliary equations for the deaerator.
175
+
176
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
177
+ the following auxiliary cost relations:
178
+
179
+ (1) Mixing equation for chemical exergy costs (if enabled):
180
+ - The outlet's specific chemical exergy cost is calculated as a mass-weighted
181
+ average of the inlet streams' specific chemical exergy costs
182
+ - This enforces proper chemical exergy cost distribution through the deaerator
183
+
184
+ (2) Mixing equation for mechanical exergy costs:
185
+ - The outlet's specific mechanical exergy cost is calculated as a mass-weighted
186
+ average of the inlet streams' specific mechanical exergy costs
187
+ - This ensures mechanical exergy costs are properly conserved in the mixing process
188
+
189
+ Both equations implement the proportionality rule for mixing processes where
190
+ the outlet's specific costs should reflect the contribution of each inlet stream.
191
+
192
+ Parameters
193
+ ----------
194
+ A : numpy.ndarray
195
+ The current cost matrix.
196
+ b : numpy.ndarray
197
+ The current right-hand-side vector.
198
+ counter : int
199
+ The current row index in the matrix.
200
+ T0 : float
201
+ Ambient temperature (provided for consistency; not used in this function).
202
+ equations : dict
203
+ Dictionary for storing equation labels.
204
+ chemical_exergy_enabled : bool
205
+ Flag indicating whether chemical exergy auxiliary equations should be added.
206
+
207
+ Returns
208
+ -------
209
+ A : numpy.ndarray
210
+ The updated cost matrix.
211
+ b : numpy.ndarray
212
+ The updated right-hand-side vector.
213
+ counter : int
214
+ The updated row index (increased by 2 if chemical exergy is enabled, or by 1 otherwise).
215
+ equations : dict
216
+ Updated dictionary with equation labels.
217
+ """
218
+ # --- Chemical cost auxiliary equation (conditionally added) ---
219
+ if chemical_exergy_enabled:
220
+ if self.outl[0]["e_CH"] != 0:
221
+ A[counter, self.outl[0]["CostVar_index"]["CH"]] = -1 / self.outl[0]["E_CH"]
222
+ # Iterate over inlet streams for chemical mixing.
223
+ for inlet in self.inl.values():
224
+ if inlet["e_CH"] != 0:
225
+ A[counter, inlet["CostVar_index"]["CH"]] = inlet["m"] / (self.outl[0]["m"] * inlet["E_CH"])
226
+ else:
227
+ A[counter, inlet["CostVar_index"]["CH"]] = 1
228
+ else:
229
+ # Outlet chemical exergy is zero: assign fallback for all inlets.
230
+ for inlet in self.inl.values():
231
+ A[counter, inlet["CostVar_index"]["CH"]] = 1
232
+ equations[counter] = f"aux_mixing_chem_{self.outl[0]['name']}"
233
+ chem_row = 1 # One row added for chemical equation.
234
+ else:
235
+ chem_row = 0 # No row added.
236
+
237
+ # --- Mechanical cost auxiliary equation ---
238
+ mech_row = 0 # This row will always be added.
239
+ if self.outl[0]["e_M"] != 0:
240
+ A[counter + chem_row, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
241
+ # Iterate over inlet streams for mechanical mixing.
242
+ for inlet in self.inl.values():
243
+ if inlet["e_M"] != 0:
244
+ A[counter + chem_row, inlet["CostVar_index"]["M"]] = inlet["m"] / (self.outl[0]["m"] * inlet["E_M"])
245
+ else:
246
+ A[counter + chem_row, inlet["CostVar_index"]["M"]] = 1
247
+ else:
248
+ for inlet in self.inl.values():
249
+ A[counter + chem_row, inlet["CostVar_index"]["M"]] = 1
250
+ equations[counter + chem_row] = f"aux_mixing_mech_{self.outl[0]['name']}"
251
+
252
+ # Set the right-hand side entries to zero for the added rows.
253
+ if chemical_exergy_enabled:
254
+ b[counter] = 0
255
+ b[counter + 1] = 0
256
+ counter += 2 # Two rows were added.
257
+ else:
258
+ b[counter] = 0
259
+ counter += 1 # Only one row was added.
260
+
261
+ return A, b, counter, equations
262
+
263
+ def exergoeconomic_balance(self, T0):
264
+ """
265
+ Perform exergoeconomic balance calculations for the deaerator.
266
+
267
+ This method calculates various exergoeconomic parameters including:
268
+ - Cost rates of product (C_P) and fuel (C_F)
269
+ - Specific cost of product (c_P) and fuel (c_F)
270
+ - Cost rate of exergy destruction (C_D)
271
+ - Relative cost difference (r)
272
+ - Exergoeconomic factor (f)
273
+
274
+ Parameters
275
+ ----------
276
+ T0 : float
277
+ Ambient temperature
278
+
279
+ Notes
280
+ -----
281
+ The exergoeconomic balance considers thermal (T), chemical (CH),
282
+ and mechanical (M) exergy components for the inlet and outlet streams.
283
+ """
284
+ self.C_P = 0
285
+ self.C_F = 0
286
+ if self.outl[0]["T"] > T0:
287
+ for i in self.inl:
288
+ if i["T"] < self.outl[0]["T"]:
289
+ # cold inlets
290
+ self.C_F += i["C_M"] + i["C_CH"]
291
+ else:
292
+ # hot inlets
293
+ self.C_F += - i["M"] * i["C_T"] * i["e_T"] + (
294
+ i["C_T"] + i["C_M"] + i["C_CH"])
295
+ self.C_F += (-self.outl[0]["C_M"] - self.outl[0]["C_CH"])
296
+ elif self.outl[0]["T"] - 1e-6 < T0 and self.outl[0]["T"] + 1e-6 > T0:
297
+ # dissipative
298
+ for i in self.inl:
299
+ self.C_F += i["C_TOT"]
300
+ else:
301
+ for i in self.inl:
302
+ if i["T"] > self.outl[0]["T"]:
303
+ # hot inlets
304
+ self.C_F += i["C_M"] + i["C_CH"]
305
+ else:
306
+ # cold inlets
307
+ self.C_F += - i["M"] * i["C_T"] * i["e_T"] + (
308
+ i["C_T"] + i["C_M"] + i["C_CH"])
309
+ self.C_F += (-self.outl[0]["C_M"] - self.outl[0]["C_CH"])
310
+ self.C_P = self.C_F + self.Z_costs # +1/num_serving_comps * C_diff
311
+ # ToDo: add case that merge profits from dissipative component(s)
312
+
313
+
314
+ self.c_F = self.C_F / self.E_F
315
+ self.c_P = self.C_P / self.E_P
316
+ self.C_D = self.c_F * self.E_D
317
+ self.r = (self.c_P - self.c_F) / self.c_F
318
+ self.f = self.Z_costs / (self.Z_costs + self.C_D)