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,123 +1,134 @@
1
1
  import logging
2
2
 
3
- from exerpy.components.component import Component
4
- from exerpy.components.component import component_registry
3
+ from exerpy.components.component import Component, component_registry
5
4
 
6
5
 
7
6
  @component_registry
8
7
  class SteamGenerator(Component):
9
8
  r"""
10
- Class for exergy analysis of steam generators.
9
+ Class for exergy and exergoeconomic analysis of heat exchangers.
11
10
 
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.
11
+ This class performs exergy and exergoeconomic analysis calculations for heat exchanger components,
12
+ accounting for two inlet and two outlet streams across various temperature regimes, including
13
+ above and below ambient temperature, and optional dissipative behavior.
65
14
 
66
15
  Attributes
67
16
  ----------
68
17
  E_F : float
69
- Exergy fuel of the component :math:`\dot{E}_F` in :math:`\mathrm{W}`.
18
+ Exergy fuel of the component :math:`\dot{E}_\mathrm{F}` in :math:`\mathrm{W}`.
70
19
  E_P : float
71
- Exergy product of the component :math:`\dot{E}_P` in :math:`\mathrm{W}`.
20
+ Exergy product of the component :math:`\dot{E}_\mathrm{P}` in :math:`\mathrm{W}`.
72
21
  E_D : float
73
- Exergy destruction of the component :math:`\dot{E}_D` in :math:`\mathrm{W}`.
22
+ Exergy destruction of the component :math:`\dot{E}_\mathrm{D}` in :math:`\mathrm{W}`.
74
23
  epsilon : float
75
- Exergetic efficiency :math:`\varepsilon`.
24
+ Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
76
25
  inl : dict
77
- Dictionary containing inlet streams.
26
+ Dictionary containing inlet stream data with mass flows and specific exergies.
78
27
  outl : dict
79
- Dictionary containing outlet streams.
28
+ Dictionary containing outlet stream data with mass flows and specific exergies.
29
+ Z_costs : float
30
+ Investment cost rate of the component in currency/h.
31
+ C_P : float
32
+ Cost of product stream :math:`\dot{C}_P` in currency/h.
33
+ C_F : float
34
+ Cost of fuel stream :math:`\dot{C}_F` in currency/h.
35
+ C_D : float
36
+ Cost of exergy destruction :math:`\dot{C}_D` in currency/h.
37
+ c_P : float
38
+ Specific cost of product stream (currency per unit exergy).
39
+ c_F : float
40
+ Specific cost of fuel stream (currency per unit exergy).
41
+ r : float
42
+ Relative cost difference, :math:`(c_P - c_F)/c_F`.
43
+ f : float
44
+ Exergoeconomic factor, :math:`\dot{Z}/(\dot{Z} + \dot{C}_D)`.
45
+ Ex_C_col : dict
46
+ Custom cost coefficients collection passed via `kwargs`.
47
+
48
+ Notes
49
+ -----
50
+ The component has several input and output streams as follows.
51
+
52
+ Inlet streams:
53
+
54
+ - inl[0]: Feed water inlet (high pressure)
55
+ - inl[1]: Steam inlet (intermediate pressure)
56
+ - inl[2]: Heat inlet (providing the heat input Q)
57
+ - inl[3]: Water injection (high pressure)
58
+ - inl[4]: Water injection (intermediate pressure)
59
+
60
+ Outlet streams:
61
+
62
+ - outl[0]: Superheated steam outlet (high pressure)
63
+ - outl[1]: Superheated steam outlet (intermediate pressure)
64
+ - outl[2]: Drain / Blow down outlet
65
+
80
66
  """
81
67
 
82
68
  def __init__(self, **kwargs):
83
- r"""Initialize steam generator component with given parameters."""
84
- self.dissipative = False
69
+ r"""
70
+ Initialize the steam generator component.
71
+
72
+ Parameters
73
+ ----------
74
+ **kwargs : dict
75
+ Arbitrary keyword arguments. Recognized keys:
76
+ - Ex_C_col (dict): custom cost coefficients, default {}
77
+ - Z_costs (float): investment cost rate in currency/h, default 0.0
78
+ """
85
79
  super().__init__(**kwargs)
86
80
 
87
81
  def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
88
82
  r"""
89
- Calculate the exergy balance of the steam generator.
83
+ Compute the exergy balance of the steam generator.
90
84
 
91
- This method computes the exergy fuel from the heat inlet using the relation
85
+ The exergy fuel is defined as follows.
86
+
87
+ If `split_physical_exergy` is `True`:
92
88
 
93
89
  .. 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}}}
90
+ \dot{E}_{\mathrm{F}}
91
+ = \bigl[\dot{E}^{\mathrm{T}}_{\mathrm{out,HP}} - \dot{E}^{\mathrm{T}}_{\mathrm{in,HP}}\bigr]
92
+ + \bigl[\dot{E}^{\mathrm{T}}_{\mathrm{out,IP}} - \dot{E}^{\mathrm{T}}_{\mathrm{in,IP}}\bigr]
93
+ - \dot{E}^{\mathrm{T}}_{\mathrm{w,HP}}
94
+ - \dot{E}^{\mathrm{T}}_{\mathrm{w,IP}}
96
95
 
97
- and the exergy product as
96
+ If `split_physical_exergy` is `False`:
98
97
 
99
98
  .. 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}}
99
+ \dot{E}_{\mathrm{F}}
100
+ = \bigl[\dot{E}^{\mathrm{PH}}_{\mathrm{out,HP}} - \dot{E}^{\mathrm{PH}}_{\mathrm{in,HP}}\bigr]
101
+ + \bigl[\dot{E}^{\mathrm{PH}}_{\mathrm{out,IP}} - \dot{E}^{\mathrm{PH}}_{\mathrm{in,IP}}\bigr]
102
+ - \dot{E}^{\mathrm{PH}}_{\mathrm{w,HP}}
103
+ - \dot{E}^{\mathrm{PH}}_{\mathrm{w,IP}}
106
104
 
107
- The exergy destruction is given by
105
+ The exergy product is defined as:
108
106
 
109
107
  .. math::
110
- E_D = E_F - E_P
111
108
 
112
- and the exergetic efficiency is
109
+ \dot{E}_\mathrm{P} = \Bigl[ \dot E^{\mathrm{PH}}_{\mathrm{out,HP}}
110
+ - \dot E^{\mathrm{PH}}_{\mathrm{in,HP}} \Bigr]
111
+ + \Bigl[ \dot E^{\mathrm{PH}}_{\mathrm{out,IP}}
112
+ - \dot E^{\mathrm{PH}}_{\mathrm{in,IP}} \Bigr]
113
+ - \dot E^{\mathrm{PH}}_{\mathrm{w,HP}}
114
+ - \dot E^{\mathrm{PH}}_{\mathrm{w,IP}}
113
115
 
114
- .. math::
115
- \varepsilon = \frac{E_P}{E_F}
116
+ where the subscripts HP and IP denote high and intermediate pressure streams,
117
+ respectively, and w stands for water injection.
116
118
 
117
119
  Parameters
118
120
  ----------
119
121
  T0 : float
120
- Ambient temperature in Kelvin.
122
+ Ambient temperature (K).
123
+ p0 : float
124
+ Ambient pressure (Pa).
125
+ split_physical_exergy : bool
126
+ Whether to split thermal and mechanical exergy.
127
+
128
+ Raises
129
+ ------
130
+ ValueError
131
+ If required inlets or outlets are missing.
121
132
  """
122
133
  # Ensure that all necessary streams exist
123
134
  required_inlets = [0]
@@ -129,34 +140,35 @@ class SteamGenerator(Component):
129
140
  if idx not in self.outl:
130
141
  raise ValueError(f"Missing outlet stream with index {idx}.")
131
142
 
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'
143
+ exergy_type = "e_T" if split_physical_exergy else "e_PH"
142
144
 
143
145
  # Calculate exergy fuel
144
146
  # 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]
147
+ E_F_HP = self.outl[0]["m"] * self.outl[0][exergy_type] - self.inl[0]["m"] * self.inl[0][exergy_type]
146
148
  # 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)
149
+ E_F_IP = self.outl.get(1, {}).get("m", 0) * self.outl.get(1, {}).get(exergy_type, 0) - self.inl.get(1, {}).get(
150
+ "m", 0
151
+ ) * self.inl.get(1, {}).get(exergy_type, 0)
148
152
  # 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)
153
+ E_F_w_inj = self.inl.get(2, {}).get("m", 0) * self.inl.get(2, {}).get(exergy_type, 0) + self.inl.get(3, {}).get(
154
+ "m", 0
155
+ ) * self.inl.get(3, {}).get(exergy_type, 0)
150
156
  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.")
157
+ logging.warning(
158
+ "Since the temperature level of the heat source of the steam generator is unknown, "
159
+ "the exergy fuel of this component is calculated based on the thermal exergy value of the water streams."
160
+ )
153
161
  # Calculate exergy product
154
162
  # 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']
163
+ E_P_HP = self.outl[0]["m"] * self.outl[0]["e_PH"] - self.inl[0]["m"] * self.inl[0]["e_PH"]
156
164
  # 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)
165
+ E_P_IP = self.outl.get(1, {}).get("m", 0) * self.outl.get(1, {}).get("e_PH", 0) - self.inl.get(1, {}).get(
166
+ "m", 0
167
+ ) * self.inl.get(1, {}).get("e_PH", 0)
158
168
  # 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)
169
+ E_P_w_inj = self.inl.get(2, {}).get("m", 0) * self.inl.get(2, {}).get("e_PH", 0) + self.inl.get(3, {}).get(
170
+ "m", 0
171
+ ) * self.inl.get(3, {}).get("e_PH", 0)
160
172
  self.E_P = E_P_HP + E_P_IP - E_P_w_inj
161
173
 
162
174
  # Calculate exergy destruction and efficiency
@@ -165,31 +177,39 @@ class SteamGenerator(Component):
165
177
 
166
178
  # Log the results
167
179
  logging.info(
168
- f"SteamGenerator exergy balance calculated: "
180
+ f"Exergy balance of SteamGenerator {self.name} calculated: "
169
181
  f"E_P = {self.E_P:.2f} W, E_F = {self.E_F:.2f} W, "
170
182
  f"E_D = {self.E_D:.2f} W, Efficiency = {self.epsilon:.2%}"
171
183
  )
172
184
 
173
-
174
185
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
186
+ r"""
187
+ This function must be implemented in the future.
188
+
189
+ The exergoeconomic analysis of SteamGenerator is not implemented yet.
190
+ """
191
+ logging.error(
192
+ "The exergoeconomic analysis of SteamGenerator is not implemented yet. "
193
+ "This method will be implemented in a future release."
194
+ )
175
195
  """
176
196
  Auxiliary equations for the steam generator.
177
-
197
+
178
198
  This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
179
199
  the following auxiliary cost relations:
180
-
200
+
181
201
  (1) c_T(heat_source)/E_F = c_T(HP_outlet)/E_T(HP) + c_T(IP_outlet)/E_T(IP)
182
202
  - P-principle: thermal exergy costs from heat source are distributed to steam outlets
183
-
203
+
184
204
  (2) 1/E_M_in(HP) * C_M_in(HP) - 1/E_M_out(HP) * C_M_out(HP) = 0
185
205
  - F-principle: specific mechanical exergy costs equalized between HP inlet/outlet
186
-
206
+
187
207
  (3) 1/E_M_in(IP) * C_M_in(IP) - 1/E_M_out(IP) * C_M_out(IP) = 0
188
208
  - F-principle: specific mechanical exergy costs equalized between IP inlet/outlet
189
-
209
+
190
210
  (4-5) Chemical exergy cost equations (if enabled) for HP and IP streams
191
211
  - F-principle: specific chemical exergy costs equalized between inlets/outlets
192
-
212
+
193
213
  Parameters
194
214
  ----------
195
215
  A : numpy.ndarray
@@ -204,7 +224,7 @@ class SteamGenerator(Component):
204
224
  Dictionary for storing equation labels.
205
225
  chemical_exergy_enabled : bool
206
226
  Flag indicating whether chemical exergy auxiliary equations should be added.
207
-
227
+
208
228
  Returns
209
229
  -------
210
230
  A : numpy.ndarray
@@ -217,48 +237,58 @@ class SteamGenerator(Component):
217
237
  Updated dictionary with equation labels.
218
238
  """
219
239
 
220
- def exergoeconomic_balance(self, T0):
240
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
241
+ r"""
242
+ This function must be implemented in the future.
243
+
244
+ The exergoeconomic analysis of SteamGenerator is not implemented yet.
245
+ """
246
+
247
+ logging.error(
248
+ "The exergoeconomic analysis of SteamGenerator is not implemented yet. "
249
+ "This method will be implemented in a future release."
250
+ )
221
251
  """
222
252
  Perform exergoeconomic balance calculations for the steam generator.
223
-
253
+
224
254
  This method calculates various exergoeconomic parameters including:
225
255
  - Cost rates of product (C_P) and fuel (C_F)
226
256
  - Specific cost of product (c_P) and fuel (c_F)
227
257
  - Cost rate of exergy destruction (C_D)
228
258
  - Relative cost difference (r)
229
259
  - Exergoeconomic factor (f)
230
-
260
+
231
261
  Parameters
232
262
  ----------
233
263
  T0 : float
234
264
  Ambient temperature
235
-
265
+ chemical_exergy_enabled : bool, optional
266
+ If True, chemical exergy is considered in the calculations.
267
+
236
268
  Notes
237
269
  -----
238
270
  The exergoeconomic balance considers thermal (T), chemical (CH),
239
271
  and mechanical (M) exergy components for the inlet and outlet streams.
240
272
  """
241
273
  # 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'])
274
+ C_P_hp = self.outl[0]["m"] * self.outl[0]["C_PH"] - self.inl[0]["m"] * self.inl[0]["C_PH"]
244
275
  C_P_ip = 0.0
245
276
  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'])
277
+ C_P_ip = self.outl[1]["m"] * self.outl[1]["C_PH"] - self.inl[1]["m"] * self.inl[1]["C_PH"]
248
278
  # Subtract water injection costs
249
279
  C_P_w = 0.0
250
280
  if 3 in self.inl:
251
- C_P_w += self.inl[3]['m'] * self.inl[3]['C_PH']
281
+ C_P_w += self.inl[3]["m"] * self.inl[3]["C_PH"]
252
282
  if 4 in self.inl:
253
- C_P_w += self.inl[4]['m'] * self.inl[4]['C_PH']
283
+ C_P_w += self.inl[4]["m"] * self.inl[4]["C_PH"]
254
284
  self.C_P = C_P_hp + C_P_ip - C_P_w
255
285
 
256
286
  # 2) Fuel cost rate: cost of heat exergy stream
257
- self.C_F = self.inl[2]['C_T']
287
+ self.C_F = self.inl[2]["C_T"]
258
288
 
259
289
  # 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')
290
+ self.c_F = self.C_F / self.E_F if self.E_F != 0 else float("nan")
291
+ self.c_P = self.C_P / self.E_P if self.E_P != 0 else float("nan")
262
292
  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')
293
+ self.r = (self.c_P - self.c_F) / self.c_F if self.c_F != 0 else float("nan")
294
+ self.f = self.Z_costs / (self.Z_costs + self.C_D) if (self.Z_costs + self.C_D) != 0 else float("nan")
@@ -2,15 +2,15 @@ import logging
2
2
 
3
3
  import numpy as np
4
4
 
5
- from exerpy.components.component import Component
6
- from exerpy.components.component import component_registry
5
+ from exerpy.components.component import Component, component_registry
7
6
 
8
7
 
9
8
  @component_registry
10
9
  class CycleCloser(Component):
11
10
  r"""
12
- Component for closing cycles. This component is not analyzed in exergy analysis.
11
+ Component for closing cycles. This component is not considered in exergy analysis, but it is used in exergoeconomic analysis.
13
12
  """
13
+
14
14
  def __init__(self, **kwargs):
15
15
  r"""Initialize CycleCloser component with given parameters."""
16
16
  super().__init__(**kwargs)
@@ -18,7 +18,7 @@ class CycleCloser(Component):
18
18
  def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
19
19
  r"""
20
20
  The CycleCloser component does not have an exergy balance calculation.
21
- """
21
+ """
22
22
  self.E_D = np.nan
23
23
  self.E_F = np.nan
24
24
  self.E_P = np.nan
@@ -26,24 +26,21 @@ class CycleCloser(Component):
26
26
  self.epsilon = np.nan
27
27
 
28
28
  # Log the results
29
- logging.info(
30
- f"The exergy balance of a CycleCloser component is skipped."
31
- )
29
+ logging.info(f"The exergy balance of a CycleCloser {self.name} is skipped.")
32
30
 
33
-
34
31
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
35
32
  """
36
33
  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
34
+
35
+ This function adds two rows to the cost matrix A and the right-hand side vector b to enforce
39
36
  the following auxiliary cost relations:
40
-
37
+
41
38
  (1) 1/E_M_in * C_M_in - 1/E_M_out * C_M_out = 0
42
39
  (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
40
+
41
+ These equations ensure that the specific mechanical and thermal costs are equalized between
45
42
  the inlet and outlet of the cycle closer. Chemical exergy is not considered for the cycle closer.
46
-
43
+
47
44
  Parameters
48
45
  ----------
49
46
  A : numpy.ndarray
@@ -59,7 +56,7 @@ class CycleCloser(Component):
59
56
  chemical_exergy_enabled : bool
60
57
  Flag indicating whether chemical exergy auxiliary equations should be added.
61
58
  This flag is ignored for CycleCloser.
62
-
59
+
63
60
  Returns
64
61
  -------
65
62
  A : numpy.ndarray
@@ -74,31 +71,61 @@ class CycleCloser(Component):
74
71
  # Mechanical cost equality equation:
75
72
  A[counter, self.inl[0]["CostVar_index"]["M"]] = (1 / self.inl[0]["e_M"]) if self.inl[0]["e_M"] != 0 else 1
76
73
  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"
74
+ equations[counter] = {
75
+ "kind": "aux_equality",
76
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
77
+ "property": "c_M",
78
+ }
78
79
  b[counter] = 0
79
80
 
80
81
  # 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
82
+ A[counter + 1, self.inl[0]["CostVar_index"]["T"]] = (1 / self.inl[0]["e_T"]) if self.inl[0]["e_T"] != 0 else 1
83
+ A[counter + 1, self.outl[0]["CostVar_index"]["T"]] = (
84
+ (-1 / self.outl[0]["e_T"]) if self.outl[0]["e_T"] != 0 else -1
85
+ )
86
+ equations[counter + 1] = {
87
+ "kind": "aux_equality",
88
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
89
+ "property": "c_T",
90
+ }
91
+ b[counter + 1] = 0
85
92
 
86
93
  counter += 2
94
+
95
+ if chemical_exergy_enabled:
96
+ # Chemical cost equality equation:
97
+ A[counter, self.inl[0]["CostVar_index"]["CH"]] = (1 / self.inl[0]["e_C"]) if self.inl[0]["e_CH"] != 0 else 1
98
+ A[counter, self.outl[0]["CostVar_index"]["CH"]] = (
99
+ (-1 / self.outl[0]["e_C"]) if self.outl[0]["e_CH"] != 0 else -1
100
+ )
101
+ equations[counter] = {
102
+ "kind": "aux_equality",
103
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
104
+ "property": "c_CH",
105
+ }
106
+ b[counter] = 0
107
+
108
+ counter += 1
109
+
87
110
  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.
111
+
112
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False) -> None:
96
113
  """
114
+ Exergoeconomic balance for the CycleCloser is not defined.
115
+
116
+ This component does not generate or consume exergy, so all cost terms are undefined.
97
117
 
118
+ Parameters
119
+ ----------
120
+ T0 : float
121
+ Ambient temperature (unused).
122
+ chemical_exergy_enabled : bool, optional
123
+ If True, chemical exergy is considered in the calculations.
124
+ """
125
+ self.C_F = np.nan
98
126
  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
127
+ self.C_D = np.nan
128
+ self.c_TOT = np.nan
129
+ self.C_TOT = np.nan
130
+ self.r = np.nan
131
+ self.f = np.nan
@@ -0,0 +1,117 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+
5
+ from exerpy.components.component import Component, component_registry
6
+
7
+
8
+ @component_registry
9
+ class PowerBus(Component):
10
+ r"""
11
+ Component for power busses. This component is not considered in exergy analysis, but it is used in exergoeconomic analysis.
12
+ """
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 PowerBus 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(f"The exergy balance of a PowerBus {self.name} is skipped.")
30
+
31
+ def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
32
+ """
33
+ Auxiliary equations for the cycle closer.
34
+
35
+ This function adds two rows to the cost matrix A and the right-hand side vector b to enforce
36
+ the following auxiliary cost relations:
37
+
38
+ (1) 1/E_M_in * C_M_in - 1/E_M_out * C_M_out = 0
39
+ (2) 1/E_T_in * C_T_in - 1/E_T_out * C_T_out = 0
40
+
41
+ These equations ensure that the specific mechanical and thermal costs are equalized between
42
+ the inlet and outlet of the cycle closer. Chemical exergy is not considered for the cycle closer.
43
+
44
+ Parameters
45
+ ----------
46
+ A : numpy.ndarray
47
+ The current cost matrix.
48
+ b : numpy.ndarray
49
+ The current right-hand-side vector.
50
+ counter : int
51
+ The current row index in the matrix.
52
+ T0 : float
53
+ Ambient temperature (not used in this component).
54
+ equations : list or dict
55
+ Data structure for storing equation labels.
56
+ chemical_exergy_enabled : bool
57
+ Flag indicating whether chemical exergy auxiliary equations should be added.
58
+ This flag is ignored for CycleCloser.
59
+
60
+ Returns
61
+ -------
62
+ A : numpy.ndarray
63
+ The updated cost matrix.
64
+ b : numpy.ndarray
65
+ The updated right-hand-side vector.
66
+ counter : int
67
+ The updated row index (increased by 2).
68
+ equations : list or dict
69
+ Updated structure with equation labels.
70
+ """
71
+
72
+ # Splitter case
73
+ if len(self.inl) >= 1 and len(self.outl) <= 1:
74
+ logging.info(f"PowerBus {self.name} has only one output, no auxiliary equations added.")
75
+
76
+ # Mixer case
77
+ elif len(self.inl) == 1 and len(self.outl) > 1:
78
+ logging.info(f"PowerBus {self.name} has multiple outputs, auxiliary equations will be added.")
79
+ for out in list(self.outl.values())[:]:
80
+ A[counter, self.inl[0]["CostVar_index"]["exergy"]] = (
81
+ (1 / self.inl[0]["E"]) if self.inl[0]["E"] != 0 else 1
82
+ )
83
+ A[counter, out["CostVar_index"]["exergy"]] = (-1 / out["E"]) if out["E"] != 0 else -1
84
+ equations[counter] = {
85
+ "kind": "aux_power_eq",
86
+ "objects": [self.name, self.inl[0]["name"], out["name"]],
87
+ "property": "c_TOT",
88
+ }
89
+ b[counter] = 0
90
+ counter += 1
91
+
92
+ # Mixer case with multiple inputs and outputs
93
+ else:
94
+ logging.error(f"PowerBus {self.name} has multiple inputs and outputs, which has not been implemented yet.")
95
+
96
+ return A, b, counter, equations
97
+
98
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False) -> None:
99
+ """
100
+ Exergoeconomic balance for the PowerBus is not defined.
101
+
102
+ This component does not convert or destroy exergy, so all cost terms are undefined.
103
+
104
+ Parameters
105
+ ----------
106
+ T0 : float
107
+ Ambient temperature (unused).
108
+ chemical_exergy_enabled : bool, optional
109
+ If True, chemical exergy is considered in the calculations.
110
+ """
111
+ self.C_F = np.nan
112
+ self.C_P = np.nan
113
+ self.C_D = np.nan
114
+ self.c_TOT = np.nan
115
+ self.C_TOT = np.nan
116
+ self.r = np.nan
117
+ self.f = np.nan