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,394 @@
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 Valve(Component):
11
+ r"""
12
+ Class for exergy analysis of valves.
13
+
14
+ This class performs exergy analysis calculations for isenthalpic valves with
15
+ one inlet and one outlet stream. The exergy product and fuel definitions
16
+ vary based on the temperature relationships between inlet stream, outlet stream,
17
+ 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 stream 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, thermal, and mechanical exergy
44
+ based on temperature relationships:
45
+
46
+ .. math::
47
+
48
+ \dot{E}_\mathrm{P} =
49
+ \begin{cases}
50
+ \mathrm{not defined (nan)}
51
+ & T_\mathrm{in}, T_\mathrm{out} > T_0\\
52
+ \dot{m} \cdot e_\mathrm{out}^\mathrm{T}
53
+ & T_\mathrm{in} > T_0 \geq T_\mathrm{out}\\
54
+ \dot{m} \cdot (e_\mathrm{out}^\mathrm{T} - e_\mathrm{in}^\mathrm{T})
55
+ & T_0 \geq T_\mathrm{in}, T_\mathrm{out}
56
+ \end{cases}
57
+
58
+ \dot{E}_\mathrm{F} =
59
+ \begin{cases}
60
+ \dot{m} \cdot (e_\mathrm{in}^\mathrm{PH} - e_\mathrm{out}^\mathrm{PH})
61
+ & T_\mathrm{in}, T_\mathrm{out} > T_0\\
62
+ \dot{m} \cdot (e_\mathrm{in}^\mathrm{T} + e_\mathrm{in}^\mathrm{M}
63
+ - e_\mathrm{out}^\mathrm{M})
64
+ & T_\mathrm{in} > T_0 \geq T_\mathrm{out}\\
65
+ \dot{m} \cdot (e_\mathrm{in}^\mathrm{M} - e_\mathrm{out}^\mathrm{M})
66
+ & T_0 \geq T_\mathrm{in}, T_\mathrm{out}
67
+ \end{cases}
68
+
69
+ For all cases, except when :math:`T_\mathrm{out} > T_\mathrm{in}`, the exergy
70
+ destruction is calculated as:
71
+
72
+ .. math::
73
+ \dot{E}_\mathrm{D} = \begin{cases}
74
+ \dot{E}_\mathrm{F} & \mathrm{if } \dot{E}_\mathrm{P} = \mathrm{nan}\\
75
+ \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P} & \mathrm{otherwise}
76
+ \end{cases}
77
+
78
+ Where:
79
+ - :math:`e^\mathrm{T}`: Thermal exergy
80
+ - :math:`e^\mathrm{PH}`: Physical exergy
81
+ - :math:`e^\mathrm{M}`: Mechanical exergy
82
+ """
83
+
84
+ def __init__(self, **kwargs):
85
+ r"""Initialize valve component with given parameters."""
86
+ super().__init__(**kwargs)
87
+
88
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
89
+ r"""
90
+ Calculate the exergy balance of the valve.
91
+
92
+ Performs exergy balance calculations considering the temperature relationships
93
+ between inlet stream, outlet stream, and ambient conditions.
94
+
95
+ Parameters
96
+ ----------
97
+ T0 : float
98
+ Ambient temperature in :math:`\mathrm{K}`.
99
+ p0 : float
100
+ Ambient pressure in :math:`\mathrm{Pa}`.
101
+ split_physical_exergy : bool
102
+ Flag indicating whether physical exergy is split into thermal and mechanical components.
103
+
104
+ Raises
105
+ ------
106
+ ValueError
107
+ If the required inlet and outlet streams are not properly defined.
108
+ """
109
+ # Ensure that the component has both inlet and outlet streams
110
+ if len(self.inl) < 1 or len(self.outl) < 1:
111
+ raise ValueError("Valve requires at least one inlet and one outlet.")
112
+
113
+ T_in = self.inl[0]['T']
114
+ T_out = self.outl[0]['T']
115
+
116
+ # Case-specific exergy calculations
117
+ if T_in > T0 and T_out > T0 and T_in > T_out:
118
+ self.E_P = np.nan
119
+ self.E_F = self.inl[0]['m'] * (self.inl[0]['e_PH'] - self.outl[0]['e_PH'])
120
+ elif T_out <= T0 and T_in > T0:
121
+ if split_physical_exergy:
122
+ self.E_P = self.inl[0]['m'] * self.outl[0]['e_T']
123
+ self.E_F = self.inl[0]['m'] * (self.inl[0]['e_T'] + self.inl[0]['e_M'] -
124
+ self.outl[0]['e_M'])
125
+ else:
126
+ logging.warning(
127
+ "Exergy balance of a valve, where outlet temperature is smaller than "
128
+ "ambient temperature, is not implemented for non-split physical exergy."
129
+ "Valve is treated as dissipative."
130
+ )
131
+ self.E_P = np.nan
132
+ self.E_F = self.inl[0]['m'] * (self.inl[0]['e_PH'] - self.outl[0]['e_PH'])
133
+
134
+ elif T_in <= T0 and T_out <= T0:
135
+ if split_physical_exergy:
136
+ self.E_P = self.inl[0]['m'] * (self.outl[0]['e_T'] - self.inl[0]['e_T'])
137
+ self.E_F = self.inl[0]['m'] * (self.inl[0]['e_M'] - self.outl[0]['e_M'])
138
+ else:
139
+ logging.warning(
140
+ "Exergy balance of a valve, where both temperatures are smaller than "
141
+ "ambient temperature, is not implemented for non-split physical exergy."
142
+ "Valve is treated as dissipative."
143
+ )
144
+ self.E_P = np.nan
145
+ self.E_F = self.inl[0]['m'] * (self.inl[0]['e_PH'] - self.outl[0]['e_PH'])
146
+ else:
147
+ logging.warning(
148
+ "Exergy balance of a valve, where outlet temperature is larger than "
149
+ "inlet temperature, is not implemented."
150
+ )
151
+ self.E_P = np.nan
152
+ self.E_F = np.nan
153
+
154
+ # Calculate exergy destruction
155
+ if np.isnan(self.E_P):
156
+ self.E_D = self.E_F
157
+ else:
158
+ self.E_D = self.E_F - self.E_P
159
+
160
+ # Calculate exergy efficiency
161
+ self.epsilon = self.calc_epsilon()
162
+
163
+ # Log the results
164
+ logging.info(
165
+ f"Valve exergy balance calculated: "
166
+ f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
167
+ f"Efficiency={self.epsilon:.2%}"
168
+ )
169
+
170
+
171
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
172
+ """
173
+ Auxiliary equations for the valve.
174
+
175
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
176
+ the following auxiliary cost relations:
177
+
178
+ For T_in > T0 and T_out > T0:
179
+ - Valve is treated as dissipative (warning issued)
180
+
181
+ For T_out <= T0:
182
+ (1) 1/E_M_in * C_M_in - 1/E_M_out * C_M_out = 0
183
+ - F-principle: specific mechanical exergy costs equalized between inlet/outlet
184
+ - If E_M is zero for either stream, appropriate fallback coefficients are used
185
+
186
+ When chemical_exergy_enabled is True:
187
+ (2) 1/E_CH_in * C_CH_in - 1/E_CH_out * C_CH_out = 0
188
+ - F-principle: specific chemical exergy costs equalized between inlet/outlet
189
+ - If E_CH is zero for either stream, appropriate fallback coefficients are used
190
+
191
+ Parameters
192
+ ----------
193
+ A : numpy.ndarray
194
+ The current cost matrix.
195
+ b : numpy.ndarray
196
+ The current right-hand-side vector.
197
+ counter : int
198
+ The current row index in the matrix.
199
+ T0 : float
200
+ Ambient temperature.
201
+ equations : dict
202
+ Dictionary for storing equation labels.
203
+ chemical_exergy_enabled : bool
204
+ Flag indicating whether chemical exergy auxiliary equations should be added.
205
+
206
+ Returns
207
+ -------
208
+ A : numpy.ndarray
209
+ The updated cost matrix.
210
+ b : numpy.ndarray
211
+ The updated right-hand-side vector.
212
+ counter : int
213
+ The updated row index.
214
+ equations : dict
215
+ Updated dictionary with equation labels.
216
+ """
217
+ if self.inl[0]["T"] > T0 and self.outl[0]["T"] > T0:
218
+ logging.warning("This case is not implemented. The Valve should be treated as dissipative!")
219
+
220
+ elif self.outl[0]["T"] <= T0:
221
+ # --- Mechanical cost equation (always added) ---
222
+ if self.inl[0]["e_M"] != 0 and self.outl[0]["e_M"] != 0:
223
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1 / self.inl[0]["E_M"]
224
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
225
+ elif self.inl[0]["e_M"] == 0 and self.outl[0]["e_M"] != 0:
226
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1
227
+ elif self.inl[0]["e_M"] != 0 and self.outl[0]["e_M"] == 0:
228
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = 1
229
+ else:
230
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1
231
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = -1
232
+ equations[counter] = f"aux_{self.name}_mech_{self.outl[0]['name']}"
233
+ b[counter] = 0
234
+ counter += 1
235
+ else:
236
+ msg = ('Exergy balance of a valve, where outlet temperature is larger than inlet temperature is not implemented.')
237
+ logging.warning(msg)
238
+
239
+ if chemical_exergy_enabled:
240
+ # --- Chemical cost equation (conditionally added) ---
241
+ A[counter, self.inl[0]["CostVar_index"]["CH"]] = (1 / self.inl[0]["E_CH"] if self.inl[0]["e_CH"] != 0 else 1)
242
+ A[counter, self.outl[0]["CostVar_index"]["CH"]] = (-1 / self.outl[0]["E_CH"] if self.outl[0]["e_CH"] != 0 else -1)
243
+ equations[counter+1] = f"aux_{self.name}_chem_{self.outl[0]['name']}"
244
+ # Set right-hand side for both rows.
245
+ b[counter] = 0
246
+ counter += 1
247
+
248
+ return A, b, counter, equations
249
+
250
+ def dis_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled=False, all_components=None):
251
+ """
252
+ Constructs the cost equations for a dissipative Valve in ExerPy,
253
+ distributing the valve’s extra cost difference (C_diff) to all other productive
254
+ components (non-dissipative and non-CycleCloser) in proportion to their exergy destruction (E_D)
255
+ and adding an extra overall cost balance row that enforces:
256
+
257
+ (C_in,T - C_out,T) + (C_in,M - C_out,M) - C_diff = - Z_costs
258
+
259
+ In this formulation, the unknown cost variable in the "dissipative" column (i.e. C_diff)
260
+ is solved for, ensuring the valve’s cost balance.
261
+
262
+ Parameters
263
+ ----------
264
+ A : numpy.ndarray
265
+ The current cost matrix.
266
+ b : numpy.ndarray
267
+ The current right-hand-side vector.
268
+ counter : int
269
+ The current row index in the cost matrix.
270
+ T0 : float
271
+ Ambient temperature (not explicitly used here).
272
+ equations : dict
273
+ Dictionary mapping row indices to equation labels.
274
+ chemical_exergy_enabled : bool, optional
275
+ Flag indicating whether chemical exergy is considered. (Ignored here.)
276
+ all_components : list, optional
277
+ Global list of all component objects; if not provided, defaults to [].
278
+
279
+ Returns
280
+ -------
281
+ tuple
282
+ Updated (A, b, counter, equations).
283
+
284
+ Notes
285
+ -----
286
+ - It is assumed that each inlet/outlet stream’s CostVar_index dictionary has keys:
287
+ "T" (thermal), "M" (mechanical), and "dissipative" (the extra unknown).
288
+ - self.Z_costs is the known cost rate (in currency/s) for the valve.
289
+ """
290
+ # --- Thermal difference row ---
291
+ if self.inl[0].get("E_T", 0) and self.outl[0].get("E_T", 0):
292
+ A[counter, self.inl[0]["CostVar_index"]["T"]] = 1 / self.inl[0]["E_T"]
293
+ A[counter, self.outl[0]["CostVar_index"]["T"]] = -1 / self.outl[0]["E_T"]
294
+ else:
295
+ A[counter, self.inl[0]["CostVar_index"]["T"]] = 1
296
+ A[counter, self.outl[0]["CostVar_index"]["T"]] = -1
297
+ b[counter] = 0
298
+ equations[counter] = f"diss_valve_thermal_{self.name}"
299
+ counter += 1
300
+
301
+ # --- Mechanical difference row ---
302
+ if self.inl[0].get("E_M", 0) and self.outl[0].get("E_M", 0):
303
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1 / self.inl[0]["E_M"]
304
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = -1 / self.outl[0]["E_M"]
305
+ else:
306
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1
307
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = -1
308
+ b[counter] = 0
309
+ equations[counter] = f"diss_valve_mechanical_{self.name}"
310
+ counter += 1
311
+
312
+ # --- Distribution of dissipative cost difference to other components based on E_D ---
313
+ if all_components is None:
314
+ all_components = []
315
+ # Serving components: all productive components (excluding self, any dissipative, and CycleCloser)
316
+ serving = [comp for comp in all_components
317
+ if comp is not self
318
+ and not getattr(comp, "dissipative", False)
319
+ and comp.__class__.__name__ != "CycleCloser"]
320
+ total_E_D = sum(getattr(comp, "E_D", 0) for comp in serving)
321
+ diss_col = self.inl[0]["CostVar_index"].get("dissipative")
322
+ if diss_col is None:
323
+ logging.warning(f"No 'dissipative' column allocated for {self.name}.")
324
+ else:
325
+ if total_E_D == 0:
326
+ # Fall back to equal distribution if total exergy destruction is zero.
327
+ for comp in serving:
328
+ A[comp.exergy_cost_line, diss_col] += 1 / len(serving) if len(serving) > 0 else 0
329
+ else:
330
+ for comp in serving:
331
+ weight = getattr(comp, "E_D", 0) / total_E_D
332
+ A[comp.exergy_cost_line, diss_col] += weight
333
+
334
+ # --- Extra overall cost balance row ---
335
+ # This row enforces:
336
+ # (C_in,T - C_out,T) + (C_in,M - C_out,M) - C_diff = - Z_costs
337
+ A[counter, self.inl[0]["CostVar_index"]["T"]] = 1
338
+ A[counter, self.outl[0]["CostVar_index"]["T"]] = -1
339
+ A[counter, self.inl[0]["CostVar_index"]["M"]] = 1
340
+ A[counter, self.outl[0]["CostVar_index"]["M"]] = -1
341
+ # Subtract the unknown dissipative cost difference:
342
+ A[counter, self.inl[0]["CostVar_index"]["dissipative"]] = -1
343
+ b[counter] = -self.Z_costs
344
+ equations[counter] = f"diss_valve_balance_{self.name}"
345
+ counter += 1
346
+
347
+ return A, b, counter, equations
348
+
349
+
350
+
351
+ def exergoeconomic_balance(self, T0):
352
+ """
353
+ Perform exergoeconomic balance calculations for the valve.
354
+
355
+ This method calculates various exergoeconomic parameters including:
356
+ - Cost rates of product (C_P) and fuel (C_F)
357
+ - Specific cost of product (c_P) and fuel (c_F)
358
+ - Cost rate of exergy destruction (C_D)
359
+ - Relative cost difference (r)
360
+ - Exergoeconomic factor (f)
361
+
362
+ Parameters
363
+ ----------
364
+ T0 : float
365
+ Ambient temperature
366
+
367
+ Notes
368
+ -----
369
+ The exergoeconomic balance considers thermal (T), chemical (CH),
370
+ and mechanical (M) exergy components for the inlet and outlet streams.
371
+ """
372
+ if self.inl[0]["T"] > T0 and self.outl[0]["T"] > T0:
373
+ self.C_F = self.inl[0]['C_PH'] - self.outl[0]['C_PH']
374
+ self.C_P = np.nan
375
+ # dissipative
376
+ elif self.outl[0]["T"] <= T0 and self.inl[0]["T"] > T0:
377
+ self.C_P = self.outl[0]["C_T"]
378
+ self.C_F = self.inl[0]["C_T"] + (
379
+ self.inl[0]["C_M"] - self.outl[0]["C_M"])
380
+ elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] <= T0:
381
+ self.C_P = self.outl[0]["C_T"] - self.inl[0]["C_T"]
382
+ self.C_F = self.inl[0]["C_M"] - self.outl[0]["C_M"]
383
+ else:
384
+ msg = ('Exergy balance of a valve, where outlet temperature is '
385
+ 'larger than inlet temperature is not implmented.')
386
+ logging.warning(msg)
387
+ self.C_P = np.nan
388
+ self.C_F = np.nan
389
+
390
+ self.c_F = self.C_F / self.E_F
391
+ self.c_P = self.C_P / self.E_P
392
+ self.C_D = self.c_F * self.E_D
393
+ self.r = (self.c_P - self.c_F) / self.c_F
394
+ self.f = self.Z_costs / (self.Z_costs + self.C_D)
File without changes
@@ -0,0 +1,168 @@
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 Generator(Component):
9
+ r"""
10
+ Class for exergy analysis of generators.
11
+
12
+ This class performs exergy analysis calculations for generators, converting mechanical
13
+ or thermal energy flow into electrical energy. The exergy product is defined as
14
+ the electrical power output, while the exergy fuel is the input energy flow.
15
+
16
+ Parameters
17
+ ----------
18
+ **kwargs : dict
19
+ Arbitrary keyword arguments passed to parent class.
20
+
21
+ Attributes
22
+ ----------
23
+ E_F : float
24
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
25
+ E_P : float
26
+ Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
27
+ E_D : float
28
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
29
+ epsilon : float
30
+ Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
31
+ inl : dict
32
+ Dictionary containing inlet stream data with energy flow.
33
+ outl : dict
34
+ Dictionary containing outlet stream data with energy flow.
35
+
36
+ Notes
37
+ -----
38
+ The exergy analysis for a generator is straightforward as electrical energy
39
+ is pure exergy. The equations are:
40
+
41
+ .. math::
42
+
43
+ \dot{E}_\mathrm{P} & = \dot{W}_\mathrm{el}
44
+
45
+ \dot{E}_\mathrm{F} & = \dot{W}_\mathrm{in}
46
+
47
+ \dot{E}_\mathrm{D} & = \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P}
48
+
49
+ where:
50
+ - :math:`\dot{W}_\mathrm{el}`: Electrical power output
51
+ - :math:`\dot{W}_\mathrm{in}`: Input power
52
+ """
53
+
54
+ def __init__(self, **kwargs):
55
+ r"""Initialize generator component with given parameters."""
56
+ super().__init__(**kwargs)
57
+ # Ex_C_col will be assigned by ExergoeconomicAnalysis.run()
58
+ self.Ex_C_col = {}
59
+
60
+ def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
61
+ r"""
62
+ Calculate the exergy balance of the generator.
63
+
64
+ Calculates the exergy product (electrical power output), exergy fuel (input power),
65
+ and the resulting exergy destruction and efficiency.
66
+
67
+ Parameters
68
+ ----------
69
+ T0 : float
70
+ Ambient temperature in :math:`\mathrm{K}`.
71
+ p0 : float
72
+ Ambient pressure in :math:`\mathrm{Pa}`.
73
+ split_physical_exergy : bool
74
+ Flag indicating whether physical exergy is split into thermal and mechanical components.
75
+
76
+ """
77
+ # Exergy product is the electrical power output
78
+ self.E_P = self.outl[0]['energy_flow']
79
+
80
+ # Exergy fuel is the input power
81
+ self.E_F = self.inl[0]['energy_flow']
82
+
83
+ # Calculate exergy destruction
84
+ self.E_D = self.E_F - self.E_P
85
+
86
+ # Calculate exergy efficiency
87
+ self.epsilon = self.calc_epsilon()
88
+
89
+ # Log the results
90
+ logging.info(
91
+ f"Generator exergy balance calculated: "
92
+ f"Generator exergy balance calculated: "
93
+ f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
94
+ f"Efficiency={self.epsilon:.2%}"
95
+ )
96
+
97
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
98
+ """
99
+ Auxiliary equations for the generator.
100
+
101
+ This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
102
+ the auxiliary cost relations for the generator. Since the generator converts mechanical
103
+ or thermal energy to electrical energy, the auxiliary equations typically enforce:
104
+
105
+ - No additional auxiliary equations are needed for generators as electrical energy
106
+ is pure exergy and the cost balance equations are sufficient.
107
+
108
+ Parameters
109
+ ----------
110
+ A : numpy.ndarray
111
+ The current cost matrix.
112
+ b : numpy.ndarray
113
+ The current right-hand-side vector.
114
+ counter : int
115
+ The current row index in the matrix.
116
+ T0 : float
117
+ Ambient temperature.
118
+ equations : dict
119
+ Dictionary for storing equation labels.
120
+ chemical_exergy_enabled : bool
121
+ Flag indicating whether chemical exergy auxiliary equations should be added.
122
+
123
+ Returns
124
+ -------
125
+ A : numpy.ndarray
126
+ The updated cost matrix.
127
+ b : numpy.ndarray
128
+ The updated right-hand-side vector.
129
+ counter : int
130
+ The updated row index.
131
+ equations : dict
132
+ Updated dictionary with equation labels.
133
+ """
134
+
135
+ return [A, b, counter, equations]
136
+
137
+ def exergoeconomic_balance(self, T0):
138
+ """
139
+ Perform exergoeconomic balance calculations for the generator.
140
+
141
+ This method calculates various exergoeconomic parameters including:
142
+ - Cost rates of product (C_P) and fuel (C_F)
143
+ - Specific cost of product (c_P) and fuel (c_F)
144
+ - Cost rate of exergy destruction (C_D)
145
+ - Relative cost difference (r)
146
+ - Exergoeconomic factor (f)
147
+
148
+ Parameters
149
+ ----------
150
+ T0 : float
151
+ Ambient temperature
152
+
153
+ Notes
154
+ -----
155
+ The exergoeconomic balance considers thermal (T), chemical (CH),
156
+ and mechanical (M) exergy components for the inlet and outlet streams.
157
+ """
158
+ self.C_P = self.outl[0].get("C_TOT", 0)
159
+ self.C_F = self.inl[0].get("C_TOT", 0)
160
+
161
+ if self.E_P == 0 or self.E_F == 0:
162
+ raise ValueError(f"E_P or E_F is zero; cannot compute specific costs for component: {self.name}.")
163
+
164
+ self.c_P = self.C_P / self.E_P
165
+ self.c_F = self.C_F / self.E_F
166
+ self.C_D = self.c_F * self.E_D # Ensure that self.E_D is computed beforehand.
167
+ self.r = (self.C_P - self.C_F) / self.C_F
168
+ self.f = self.Z_costs / (self.Z_costs + self.C_D) if (self.Z_costs + self.C_D) != 0 else 0