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.
- exerpy/__init__.py +12 -0
- exerpy/analyses.py +1711 -0
- exerpy/components/__init__.py +16 -0
- exerpy/components/combustion/__init__.py +0 -0
- exerpy/components/combustion/base.py +248 -0
- exerpy/components/component.py +126 -0
- exerpy/components/heat_exchanger/__init__.py +0 -0
- exerpy/components/heat_exchanger/base.py +449 -0
- exerpy/components/heat_exchanger/condenser.py +323 -0
- exerpy/components/heat_exchanger/simple.py +358 -0
- exerpy/components/heat_exchanger/steam_generator.py +264 -0
- exerpy/components/helpers/__init__.py +0 -0
- exerpy/components/helpers/cycle_closer.py +104 -0
- exerpy/components/nodes/__init__.py +0 -0
- exerpy/components/nodes/deaerator.py +318 -0
- exerpy/components/nodes/drum.py +164 -0
- exerpy/components/nodes/flash_tank.py +89 -0
- exerpy/components/nodes/mixer.py +332 -0
- exerpy/components/piping/__init__.py +0 -0
- exerpy/components/piping/valve.py +394 -0
- exerpy/components/power_machines/__init__.py +0 -0
- exerpy/components/power_machines/generator.py +168 -0
- exerpy/components/power_machines/motor.py +173 -0
- exerpy/components/turbomachinery/__init__.py +0 -0
- exerpy/components/turbomachinery/compressor.py +318 -0
- exerpy/components/turbomachinery/pump.py +310 -0
- exerpy/components/turbomachinery/turbine.py +351 -0
- exerpy/data/Ahrendts.json +90 -0
- exerpy/functions.py +637 -0
- exerpy/parser/__init__.py +0 -0
- exerpy/parser/from_aspen/__init__.py +0 -0
- exerpy/parser/from_aspen/aspen_config.py +61 -0
- exerpy/parser/from_aspen/aspen_parser.py +721 -0
- exerpy/parser/from_ebsilon/__init__.py +38 -0
- exerpy/parser/from_ebsilon/check_ebs_path.py +74 -0
- exerpy/parser/from_ebsilon/ebsilon_config.py +1055 -0
- exerpy/parser/from_ebsilon/ebsilon_functions.py +181 -0
- exerpy/parser/from_ebsilon/ebsilon_parser.py +660 -0
- exerpy/parser/from_ebsilon/utils.py +79 -0
- exerpy/parser/from_tespy/tespy_config.py +23 -0
- exerpy-0.0.1.dist-info/METADATA +158 -0
- exerpy-0.0.1.dist-info/RECORD +44 -0
- exerpy-0.0.1.dist-info/WHEEL +4 -0
- 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
|