exerpy 0.0.1__py3-none-any.whl → 0.0.3__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 (39) hide show
  1. exerpy/__init__.py +2 -4
  2. exerpy/analyses.py +597 -297
  3. exerpy/components/__init__.py +3 -0
  4. exerpy/components/combustion/base.py +157 -114
  5. exerpy/components/component.py +8 -8
  6. exerpy/components/heat_exchanger/base.py +593 -256
  7. exerpy/components/heat_exchanger/condenser.py +353 -166
  8. exerpy/components/heat_exchanger/simple.py +575 -225
  9. exerpy/components/heat_exchanger/steam_generator.py +153 -123
  10. exerpy/components/helpers/cycle_closer.py +61 -34
  11. exerpy/components/helpers/power_bus.py +117 -0
  12. exerpy/components/nodes/deaerator.py +221 -102
  13. exerpy/components/nodes/drum.py +50 -39
  14. exerpy/components/nodes/flash_tank.py +218 -43
  15. exerpy/components/nodes/mixer.py +296 -115
  16. exerpy/components/nodes/splitter.py +173 -0
  17. exerpy/components/nodes/storage.py +130 -0
  18. exerpy/components/piping/valve.py +351 -139
  19. exerpy/components/power_machines/generator.py +105 -38
  20. exerpy/components/power_machines/motor.py +111 -39
  21. exerpy/components/turbomachinery/compressor.py +181 -63
  22. exerpy/components/turbomachinery/pump.py +182 -63
  23. exerpy/components/turbomachinery/turbine.py +182 -74
  24. exerpy/functions.py +388 -263
  25. exerpy/parser/from_aspen/aspen_config.py +57 -48
  26. exerpy/parser/from_aspen/aspen_parser.py +373 -280
  27. exerpy/parser/from_ebsilon/__init__.py +2 -2
  28. exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
  29. exerpy/parser/from_ebsilon/ebsilon_config.py +329 -227
  30. exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
  31. exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
  32. exerpy/parser/from_ebsilon/utils.py +16 -11
  33. exerpy/parser/from_tespy/tespy_config.py +32 -1
  34. exerpy/parser/from_tespy/tespy_parser.py +151 -0
  35. {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/METADATA +45 -4
  36. exerpy-0.0.3.dist-info/RECORD +48 -0
  37. exerpy-0.0.1.dist-info/RECORD +0 -44
  38. {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
  39. {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,169 +1,223 @@
1
1
  import logging
2
2
 
3
- from exerpy.components.component import Component
4
- from exerpy.components.component import component_registry
3
+ import numpy as np
4
+
5
+ from exerpy.components.component import Component, component_registry
5
6
 
6
7
 
7
8
  @component_registry
8
9
  class Condenser(Component):
9
- """
10
- Condenser component class.
10
+ r"""
11
+ Class for exergy and exergoeconomic analysis of condensers (only dissipative).
11
12
 
12
- This class represents a condenser within the system, responsible for
13
- calculating the exergy balance specific to condensation processes.
14
- It evaluates the exergy interactions between multiple inlet and outlet
15
- streams to determine exergy loss and exergy destruction.
13
+ This class performs exergy and exergoeconomic analysis calculations for condenser components,
14
+ accounting for two inlet and two outlet streams. This class should be used only for dissipative
15
+ condensers. For non-dissipative condensers, use components that are modeled in ExerPy using the
16
+ `HeatExchanger` class.
16
17
 
17
18
  Attributes
18
19
  ----------
19
- E_L : float
20
- Exergy loss associated with heat transfer (difference in physical exergy
21
- between specific outlet and inlet streams).
20
+ E_F : float
21
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
22
22
  E_D : float
23
- Exergy destruction, calculated as the difference between the primary
24
- inlet and outlet streams minus exergy loss (E_L), representing
25
- irreversibilities in the condensation process.
26
- E_P : None
27
- Exergy product, not defined for a condenser as there is no exergy output
28
- intended for productive use.
29
- E_F : None
30
- Exergy fuel, typically undefined for a condenser as it does not involve
31
- an external exergy input for production purposes.
32
- epsilon : None
33
- Exergy efficiency, not applicable to a condenser due to the nature of
34
- exergy interactions focused on loss and destruction.
35
-
36
- Methods
37
- -------
38
- __init__(**kwargs)
39
- Initializes the Condenser component with given parameters.
40
- calc_exergy_balance(T0, p0)
41
- Calculates the exergy balance of the condenser.
23
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
24
+ E_L : float
25
+ Exergy loss of the component :math:`\dot{E}_\mathrm{L}` in :math:`\mathrm{W}`.
26
+ inl : dict
27
+ Dictionary containing inlet stream data with mass flows and specific exergies.
28
+ outl : dict
29
+ Dictionary containing outlet stream data with mass flows and specific exergies.
30
+ Z_costs : float
31
+ Investment cost rate of the component in currency/h.
32
+ C_F : float
33
+ Cost of fuel stream :math:`\dot{C}_F` in currency/h.
34
+ C_D : float
35
+ Cost of exergy destruction :math:`\dot{C}_D` in currency/h.
36
+ c_F : float
37
+ Specific cost of fuel stream (currency per unit exergy).
38
+ f : float
39
+ Exergoeconomic factor, :math:`\dot{Z}/(\dot{Z} + \dot{C}_D)`.
40
+ Ex_C_col : dict
41
+ Custom cost coefficients collection passed via `kwargs`.
42
42
  """
43
43
 
44
44
  def __init__(self, **kwargs):
45
- """
46
- Initialize the Condenser component.
45
+ r"""
46
+ Initialize the condenser component.
47
47
 
48
48
  Parameters
49
49
  ----------
50
50
  **kwargs : dict
51
- Arbitrary keyword arguments passed to the base class initializer.
51
+ Arbitrary keyword arguments. Recognized keys:
52
+ - Ex_C_col (dict): custom cost coefficients, default {}
53
+ - Z_costs (float): investment cost rate in currency/h, default 0.0
52
54
  """
53
55
  super().__init__(**kwargs)
54
-
56
+
55
57
  def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
56
- """
57
- Calculate the exergy balance of the condenser.
58
+ r"""
59
+ Compute the exergy balance of the condenser.
60
+
61
+ In order to distinguish between the exergetic destruction because of heat transfer
62
+ and the exergetic loss (coldf stream leaving the system) the exergetic losses and
63
+ destruction are calculated as follows:
64
+
65
+ .. math::
66
+
67
+ \dot{E}_{\mathrm{L}}
68
+ = \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
69
+ - \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
58
70
 
59
- This method computes exergy loss and exergy destruction based on the inlet
60
- and outlet streams involved in the condensation process.
71
+ .. math::
72
+
73
+ \dot{E}_{\mathrm{D}}
74
+ = \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
75
+ - \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
76
+ - \dot{E}_{\mathrm{L}}
77
+
78
+ However, these value can only be accessed via the attributes `E_L` and `E_D` of the component.
79
+ In the table of final results of the exergy analysis of the system, the exergy destruction of
80
+ the condenser is counted as the exergy loss and the exergetic destruction due to heat transfer.
61
81
 
62
82
  Parameters
63
83
  ----------
64
84
  T0 : float
65
- Reference temperature in Kelvin.
85
+ Ambient temperature (K).
66
86
  p0 : float
67
- Reference pressure in Pascals.
87
+ Ambient pressure (Pa).
68
88
  split_physical_exergy : bool
69
- Flag indicating whether physical exergy is split into thermal and mechanical components.
89
+ Whether to split thermal and mechanical exergy.
70
90
 
71
91
  Raises
72
92
  ------
73
93
  ValueError
74
- If the condenser does not have exactly two inlets and two outlets.
75
-
76
- Calculation Details
77
- -------------------
78
- The exergy balance is determined based on exergy transfer due to heat loss (`E_L`)
79
- and the exergy destruction within the system:
80
-
81
- - **Exergy Loss (E_L)**:
82
- \[
83
- E_L = \dot{m}_{\mathrm{out,1}} \cdot (e_{\mathrm{PH,out,1}} - e_{\mathrm{PH,in,1}})
84
- \]
85
- Represents the exergy loss due to heat transfer from the process.
86
-
87
- - **Exergy Destruction (E_D)**:
88
- \[
89
- E_D = \dot{m}_{\mathrm{out,0}} \cdot (e_{\mathrm{PH,in,0}} - e_{\mathrm{PH,out,0}}) - E_L
90
- \]
91
- Accounts for the irreversibilities and losses in the condenser.
92
-
93
- Note
94
- ----
95
- Exergy product (E_P) and exergy fuel (E_F) are generally undefined in a
96
- condenser due to the focus on exergy loss rather than productive exergy usage.
94
+ If required inlets or outlets are missing.
97
95
  """
98
96
  # Ensure that the component has both inlet and outlet streams
99
97
  if len(self.inl) < 2 or len(self.outl) < 2:
100
98
  raise ValueError("Condenser requires two inlets and two outlets.")
101
-
99
+
102
100
  # Calculate exergy loss (E_L) for the heat transfer process
103
- self.E_L = self.outl[1]['m'] * (self.outl[1]['e_PH'] - self.inl[1]['e_PH'])
101
+ self.E_L = self.outl[1]["m"] * (self.outl[1]["e_PH"] - self.inl[1]["e_PH"])
104
102
 
105
103
  # Calculate exergy destruction (E_D)
106
- self.E_D = self.outl[0]['m'] * (self.inl[0]['e_PH'] - self.outl[0]['e_PH']) - self.E_L
104
+ self.E_D = self.outl[0]["m"] * (self.inl[0]["e_PH"] - self.outl[0]["e_PH"]) - self.E_L
107
105
 
108
106
  # Exergy fuel and product are not typically defined for a condenser
109
- self.E_F = None
110
- self.E_P = None
111
- self.epsilon = None
112
-
113
- # Log the exergy balance results
114
- logging.info(f"Condenser exergy balance calculated: E_D={self.E_D}, E_L={self.E_L}")
107
+ self.E_F = np.nan
108
+ self.E_P = np.nan
109
+ self.epsilon = np.nan
115
110
 
111
+ # Log the results
112
+ logging.info(
113
+ f"Exergy balance of Condenser {self.name} calculated: "
114
+ f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
115
+ f"Efficiency={self.epsilon:.2%}"
116
+ )
116
117
 
117
118
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
118
- """
119
- Auxiliary equations for the condenser.
120
-
121
- This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
122
- the following auxiliary cost relations:
123
-
124
- (1) Thermal auxiliary equation based on temperature cases:
125
- - Case 1 (T > T0): c_T(hot_inlet)/E_T(hot_in) = c_T(hot_outlet)/E_T(hot_out)
126
- F-principle: specific thermal exergy costs equalized in hot stream
127
- - Case 2 (T <= T0): c_T(cold_inlet)/E_T(cold_in) = c_T(cold_outlet)/E_T(cold_out)
128
- F-principle: specific thermal exergy costs equalized in cold stream
129
- - Case 3 (mixed temperatures): c_T(hot_outlet)/E_T(hot_out) = c_T(cold_outlet)/E_T(cold_out)
130
- P-principle: equal specific costs of thermal exergy in outlets
131
-
132
- (2) c_M(hot_inlet)/E_M(hot_in) = c_M(hot_outlet)/E_M(hot_out)
133
- - F-principle: specific mechanical exergy costs equalized in hot stream
134
-
135
- (3) c_M(cold_inlet)/E_M(cold_in) = c_M(cold_outlet)/E_M(cold_out)
136
- - F-principle: specific mechanical exergy costs equalized in cold stream
137
-
138
- (4-5) Chemical exergy cost equations (if enabled) for hot and cold streams
139
- - F-principle: specific chemical exergy costs equalized between inlets/outlets
140
-
119
+ r"""
120
+ Add auxiliary cost equations for the condenser.
121
+
122
+ This method appends rows to the cost matrix to enforce:
123
+
124
+ Case 1: All streams above ambient temperature
125
+
126
+ F rule for thermal exergy of the hot stream:
127
+
128
+ .. math::
129
+ -\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
130
+ + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
131
+ = 0
132
+
133
+ Case 2: All streams below or equal to ambient temperature
134
+
135
+ F rule for thermal exergy of the cold stream:
136
+
137
+ .. math::
138
+ -\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
139
+ + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},2}
140
+ = 0
141
+
142
+ Case 3: Both stream crossing ambient temperature
143
+
144
+ P rule for thermal exergy of both outlets:
145
+
146
+ .. math::
147
+ -\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
148
+ + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
149
+ = 0
150
+
151
+ Case 4: Only the hot inlet above ambient temperature
152
+
153
+ F rule for thermal exergy of the cold stream:
154
+
155
+ .. math::
156
+ -\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
157
+ + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},2}
158
+ = 0
159
+
160
+ Case 5: Only the cold inlet below ambient temperature
161
+
162
+ F rule for thermal exergy of the hot stream:
163
+
164
+ .. math::
165
+ -\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
166
+ + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
167
+ = 0
168
+
169
+ Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
170
+
171
+ The dissipative is not handeld here!
172
+
173
+ For all cases, the mechanical and chemical exergy costs are handled as follows:
174
+
175
+ F rule for mechanical exergy of the hot stream:
176
+
177
+ .. math::
178
+ -\frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{out},i}}\,\dot{C}^{\mathrm{M}}_{\mathrm{out},i}
179
+ + \frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{in},i}}\,\dot{C}^{\mathrm{M}}_{\mathrm{in},i}
180
+ = 0
181
+
182
+ F rule for chemical exergy on hot branch:
183
+
184
+ .. math::
185
+ -\frac{1}{\dot{E}^{\mathrm{CH}}_{\mathrm{out},i}}\,\dot{C}^{\mathrm{CH}}_{\mathrm{out},i}
186
+ + \frac{1}{\dot{E}^{\mathrm{CH}}_{\mathrm{in},i}}\,\dot{C}^{\mathrm{CH}}_{\mathrm{in},i}
187
+ = 0
188
+
141
189
  Parameters
142
190
  ----------
143
191
  A : numpy.ndarray
144
- The current cost matrix.
192
+ Current cost matrix.
145
193
  b : numpy.ndarray
146
- The current right-hand-side vector.
194
+ Current RHS vector.
147
195
  counter : int
148
- The current row index in the matrix.
196
+ Starting row index for auxiliary equations.
149
197
  T0 : float
150
- Ambient temperature.
151
- equations : dict
152
- Dictionary for storing equation labels.
198
+ Ambient temperature (K).
199
+ equations : dict or list
200
+ Structure for equation labels.
153
201
  chemical_exergy_enabled : bool
154
- Flag indicating whether chemical exergy auxiliary equations should be added.
155
-
202
+ Must be True to include chemical exergy mixing.
203
+
156
204
  Returns
157
205
  -------
158
206
  A : numpy.ndarray
159
- The updated cost matrix.
207
+ Updated cost matrix.
160
208
  b : numpy.ndarray
161
- The updated right-hand-side vector.
209
+ Updated RHS vector.
162
210
  counter : int
163
- The updated row index.
164
- equations : dict
165
- Updated dictionary with equation labels.
211
+ Updated row index after adding equations.
212
+ equations : dict or list
213
+ Updated labels.
214
+
215
+ Raises
216
+ ------
217
+ ValueError
218
+ If required cost variable indices are missing.
166
219
  """
220
+
167
221
  # Equality equation for mechanical and chemical exergy costs.
168
222
  def set_equal(A, row, in_item, out_item, var):
169
223
  if in_item["e_" + var] != 0 and out_item["e_" + var] != 0:
@@ -220,48 +274,101 @@ class Condenser(Component):
220
274
  # Case 1: All temperatures > T0.
221
275
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
222
276
  set_thermal_f_hot(A, counter + 0)
223
- equations[counter] = f"aux_f_rule_hot_{self.name}"
277
+ self.equations[counter] = {
278
+ "kind": "aux_f_rule_hot",
279
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
280
+ "property": "c_T",
281
+ }
224
282
  # Case 2: All temperatures <= T0.
225
- elif all([c["T"] <= T0 for c in self.inl + self.outl]):
283
+ elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
226
284
  set_thermal_f_cold(A, counter + 0)
227
- equations[counter] = f"aux_f_rule_cold_{self.name}"
228
- # Case 3: Mixed temperatures: inl[0]["T"] > T0 and outl[1]["T"] > T0, while outl[0]["T"] <= T0 and inl[1]["T"] <= T0.
229
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
230
- self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0):
285
+ self.equations[counter] = {
286
+ "kind": "aux_f_rule_cold",
287
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
288
+ "property": "c_T",
289
+ }
290
+ logging.warning(
291
+ f"All temperatures in {self.name} are below ambient temperature. "
292
+ "This is not a typical case for a dissipative condenser."
293
+ )
294
+ # Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= T0)
295
+ elif self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0:
231
296
  set_thermal_p_rule(A, counter + 0)
232
- equations[counter] = f"aux_p_rule_{self.name}"
233
- # Case 4: Mixed temperatures: inl[0]["T"] > T0, inl[1]["T"] <= T0, and both outl[0]["T"] and outl[1]["T"] <= T0.
234
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
235
- self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0):
297
+ equations[counter] = {
298
+ "kind": "aux_p_rule",
299
+ "objects": [self.name, self.outl[0]["name"], self.outl[1]["name"]],
300
+ "property": "c_T",
301
+ }
302
+ logging.warning(
303
+ f"Hot inlet and cold outlet in {self.name} are above ambient temperature, "
304
+ "while hot outlet and cold inlet are below. This is not a typical case for a dissipative condenser."
305
+ "The exergoeconomic analysis is counting the outlets as products."
306
+ )
307
+ # Case 4: Only hot inlet > T0
308
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0:
236
309
  set_thermal_f_cold(A, counter + 0)
237
- equations[counter] = f"aux_f_rule_cold_{self.name}"
238
- # Case 5: Mixed temperatures: inl[0]["T"] > T0, inl[1]["T"] <= T0, and both outl[0]["T"] and outl[1]["T"] > T0.
239
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
240
- self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0):
310
+ equations[counter] = {
311
+ "kind": "aux_f_rule_cold",
312
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
313
+ "property": "c_T",
314
+ }
315
+ logging.warning(
316
+ f"Cold inlet in {self.name} is below ambient temperature. "
317
+ "This is not a typical case for a dissipative condenser."
318
+ )
319
+ # Case 5: Only cold inlet <= T0
320
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0:
241
321
  set_thermal_f_hot(A, counter + 0)
242
- equations[counter] = f"aux_f_rule_hot_{self.name}"
243
- # Case 6: Mixed temperatures (dissipative case): inl[0]["T"] > T0, inl[1]["T"] <= T0, outl[0]["T"] > T0, and outl[1]["T"] <= T0.
244
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
245
- self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0):
322
+ equations[counter] = {
323
+ "kind": "aux_f_rule_hot",
324
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
325
+ "property": "c_T",
326
+ }
327
+ logging.warning(
328
+ f"Cold inlet in {self.name} is below ambient temperature. "
329
+ "This is not a typical case for a dissipative condenser."
330
+ )
331
+ # Case 6: hot stream always above T0, cold stream always below T0
332
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0:
246
333
  print("you shouldn't see this")
247
334
  return
248
335
  # Case 7: Default case.
249
336
  else:
250
337
  set_thermal_f_hot(A, counter + 0)
251
- equations[counter] = f"aux_f_rule_hot_{self.name}"
252
-
338
+ equations[counter] = {
339
+ "kind": "aux_f_rule_hot",
340
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
341
+ "property": "c_T",
342
+ }
343
+
253
344
  # Mechanical equations (always added)
254
345
  set_equal(A, counter + 1, self.inl[0], self.outl[0], "M")
255
346
  set_equal(A, counter + 2, self.inl[1], self.outl[1], "M")
256
- equations[counter + 1] = f"aux_equality_mech_{self.outl[0]['name']}"
257
- equations[counter + 2] = f"aux_equality_mech_{self.outl[1]['name']}"
258
-
347
+ equations[counter + 1] = {
348
+ "kind": "aux_equality",
349
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
350
+ "property": "c_M",
351
+ }
352
+ equations[counter + 2] = {
353
+ "kind": "aux_equality",
354
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
355
+ "property": "c_M",
356
+ }
357
+
259
358
  # Only add chemical auxiliary equations if chemical exergy is enabled.
260
359
  if chemical_exergy_enabled:
261
360
  set_equal(A, counter + 3, self.inl[0], self.outl[0], "CH")
262
361
  set_equal(A, counter + 4, self.inl[1], self.outl[1], "CH")
263
- equations[counter + 3] = f"aux_equality_chem_{self.outl[0]['name']}"
264
- equations[counter + 4] = f"aux_equality_chem_{self.outl[1]['name']}"
362
+ equations[counter + 3] = {
363
+ "kind": "aux_equality",
364
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
365
+ "property": "c_CH",
366
+ }
367
+ equations[counter + 4] = {
368
+ "kind": "aux_equality",
369
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
370
+ "property": "c_M",
371
+ }
265
372
  num_aux_eqs = 5
266
373
  else:
267
374
  # Skip chemical auxiliary equations.
@@ -271,50 +378,130 @@ class Condenser(Component):
271
378
  b[counter + i] = 0
272
379
 
273
380
  return A, b, counter + num_aux_eqs, equations
274
-
275
- def exergoeconomic_balance(self, T0):
276
- """
277
- Perform exergoeconomic balance calculations for the condenser.
278
-
279
- This method calculates various exergoeconomic parameters including:
280
- - Cost rates of product (C_P) and fuel (C_F)
281
- - Specific cost of product (c_P) and fuel (c_F)
282
- - Cost rate of exergy destruction (C_D)
283
- - Relative cost difference (r)
284
- - Exergoeconomic factor (f)
285
-
381
+
382
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
383
+ r"""
384
+ Perform exergoeconomic cost balance for the condenser.
385
+
386
+ Even though this class should only consider dissipative condensers, the exergoeconomic balance is
387
+ still performed to ensure consistency. Please note that same of the following cases cases are not
388
+ typical for dissipative condensers. This may change in a future version of ExerPy.
389
+
390
+ .. math::
391
+ \dot{C}^{\mathrm{T}}_{\mathrm{in},1}
392
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in},1}
393
+ + \dot{C}^{\mathrm{T}}_{\mathrm{in},2}
394
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in},2}
395
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
396
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out},1}
397
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
398
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out},2}
399
+ + \dot{Z}
400
+ = 0
401
+
402
+ In case the chemical exergy of the streams is know:
403
+
404
+ .. math::
405
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in},1} =
406
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out},1}
407
+
408
+ .. math::
409
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in},2} =
410
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out},2}
411
+
412
+ This method computes cost coefficients and ratios:
413
+
414
+ Case 1: All streams above ambient temperature
415
+
416
+ .. math::
417
+ \dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
418
+ - \dot{C}^{\mathrm{T}}_{\mathrm{in},2}
419
+
420
+ .. math::
421
+ \dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
422
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}
423
+ + \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{in},2}
424
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
425
+
426
+ Case 2: All streams below or equal to ambient temperature
427
+
428
+ .. math::
429
+ \dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
430
+ - \dot{C}^{\mathrm{T}}_{\mathrm{in},1}
431
+
432
+ .. math::
433
+ \dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
434
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
435
+ + \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{in},1}
436
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
437
+
438
+ Case 3: Both stream crossing ambient temperature
439
+
440
+ .. math::
441
+ \dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
442
+ + \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
443
+
444
+ .. math::
445
+ \dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
446
+ + \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
447
+ - \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{out},1}
448
+ + \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
449
+
450
+ Case 4: Only the hot inlet above ambient temperature
451
+
452
+ .. math::
453
+ \dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
454
+
455
+ .. math::
456
+ \dot{C}_F = \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
457
+ + \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}\bigr)
458
+ - \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
459
+ + \dot{C}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
460
+
461
+ Case 5: Only the cold inlet below ambient temperature
462
+
463
+ .. math::
464
+ \dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
465
+
466
+ .. math::
467
+ \dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
468
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}
469
+ + \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
470
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
471
+
472
+ Case 6: Hot stream always above and cold stream always below ambient temperature (dissipative case):
473
+
474
+ .. math::
475
+ \dot{C}_P = \mathrm{NaN}
476
+
477
+ .. math::
478
+ \dot{C}_F = \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
479
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
480
+ - \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
481
+ + \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
482
+
286
483
  Parameters
287
484
  ----------
288
485
  T0 : float
289
- Ambient temperature
290
-
291
- Notes
292
- -----
293
- The exergoeconomic balance considers thermal (T), chemical (CH),
294
- and mechanical (M) exergy components for the inlet and outlet streams.
486
+ Ambient temperature (K).
487
+ chemical_exergy_enabled : bool, optional
488
+ If True, chemical exergy is considered in the calculations.
295
489
  """
296
490
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
297
491
  self.C_P = self.outl[1]["C_T"] - self.inl[1]["C_T"]
298
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
299
- self.inl[1]["C_M"] - self.outl[1]["C_M"])
492
+ self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_M"] - self.outl[1]["C_M"])
300
493
  elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
301
494
  self.C_P = self.outl[0]["C_T"] - self.inl[0]["C_T"]
302
- self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (
303
- self.inl[0]["C_M"] - self.outl[0]["C_M"])
304
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
305
- self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0):
495
+ self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (self.inl[0]["C_M"] - self.outl[0]["C_M"])
496
+ elif self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0:
306
497
  self.C_P = self.outl[0]["C_T"] + self.outl[1]["C_T"]
307
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
308
- self.outl[0]["C_M"] + self.outl[1]["C_M"])
309
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
310
- self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0):
498
+ self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[0]["C_M"] + self.outl[1]["C_M"])
499
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0:
311
500
  self.C_P = self.outl[0]["C_T"]
312
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
313
- self.outl[1]["C_PH"] + self.outl[0]["C_M"])
501
+ self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[1]["C_PH"] + self.outl[0]["C_M"])
314
502
  else:
315
503
  self.C_P = self.outl[1]["C_T"]
316
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
317
- self.inl[1]["C_PH"] - self.outl[1]["C_M"])
504
+ self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_PH"] - self.outl[1]["C_M"])
318
505
 
319
506
  self.c_F = self.C_F / self.E_F
320
507
  self.c_P = self.C_P / self.E_P