exerpy 0.0.2__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 +53 -35
  5. exerpy/components/component.py +8 -8
  6. exerpy/components/heat_exchanger/base.py +186 -119
  7. exerpy/components/heat_exchanger/condenser.py +96 -60
  8. exerpy/components/heat_exchanger/simple.py +237 -137
  9. exerpy/components/heat_exchanger/steam_generator.py +46 -41
  10. exerpy/components/helpers/cycle_closer.py +61 -34
  11. exerpy/components/helpers/power_bus.py +117 -0
  12. exerpy/components/nodes/deaerator.py +176 -58
  13. exerpy/components/nodes/drum.py +50 -39
  14. exerpy/components/nodes/flash_tank.py +218 -43
  15. exerpy/components/nodes/mixer.py +249 -69
  16. exerpy/components/nodes/splitter.py +173 -0
  17. exerpy/components/nodes/storage.py +130 -0
  18. exerpy/components/piping/valve.py +311 -115
  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 +328 -226
  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.2.dist-info → exerpy-0.0.3.dist-info}/METADATA +43 -2
  36. exerpy-0.0.3.dist-info/RECORD +48 -0
  37. exerpy-0.0.2.dist-info/RECORD +0 -44
  38. {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
  39. {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -2,8 +2,7 @@ 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
@@ -88,59 +87,73 @@ class Turbine(Component):
88
87
  split_physical_exergy : bool
89
88
  Flag indicating whether physical exergy is split into thermal and mechanical components.
90
89
  """
91
- # Get power flow if not already available
92
- if self.P is None:
93
- self.P = self._total_outlet('m', 'h') - self.inl[0]['m'] * self.inl[0]['h']
90
+ # Get net power flow
91
+ net_power = 0.0 # Initialize to 0.0, not None
92
+ for idx, conn in self.inl.items():
93
+ if conn is not None and conn.get("kind") == "power" and "energy_flow" in conn:
94
+ net_power -= conn["energy_flow"] # Subtract inlet power
95
+ for idx, conn in self.outl.items():
96
+ if conn is not None and conn.get("kind") == "power" and "energy_flow" in conn:
97
+ net_power += conn["energy_flow"] # Add outlet power
98
+
99
+ # Use net_power if available, otherwise calculate from enthalpy balance
100
+ if net_power != 0.0:
101
+ self.P = abs(net_power)
102
+ else:
103
+ self.P = self._total_outlet("m", "h") - self.inl[0]["m"] * self.inl[0]["h"]
94
104
 
95
105
  # Case 1: Both temperatures above ambient
96
- if self.inl[0]['T'] >= T0 and self.outl[0]['T'] >= T0 and self.inl[0]['T'] >= self.outl[0]['T']:
106
+ if self.inl[0]["T"] >= T0 and self.outl[0]["T"] >= T0 and self.inl[0]["T"] >= self.outl[0]["T"]:
97
107
  self.E_P = abs(self.P)
98
- self.E_F = (self.inl[0]['m'] * self.inl[0]['e_PH'] -
99
- self._total_outlet('m', 'e_PH'))
108
+ self.E_F = self.inl[0]["m"] * self.inl[0]["e_PH"] - self._total_outlet("m", "e_PH")
100
109
 
101
110
  # Case 2: Inlet above, outlet at/below ambient
102
- elif self.inl[0]['T'] > T0 and self.outl[0]['T'] <= T0:
111
+ elif self.inl[0]["T"] > T0 and self.outl[0]["T"] <= T0:
103
112
  if split_physical_exergy:
104
- self.E_P = abs(self.P) + self._total_outlet('m', 'e_T')
105
- self.E_F = (self.inl[0]['m'] * self.inl[0]['e_T'] +
106
- self.inl[0]['m'] * self.inl[0]['e_M'] -
107
- self._total_outlet('m', 'e_M'))
113
+ self.E_P = abs(self.P) + self._total_outlet("m", "e_T")
114
+ self.E_F = (
115
+ self.inl[0]["m"] * self.inl[0]["e_T"]
116
+ + self.inl[0]["m"] * self.inl[0]["e_M"]
117
+ - self._total_outlet("m", "e_M")
118
+ )
108
119
  else:
109
- logging.warning("While dealing with expander below ambient, "
110
- "physical exergy should be split into thermal and mechanical components!")
120
+ logging.warning(
121
+ "While dealing with expander below ambient, "
122
+ "physical exergy should be split into thermal and mechanical components!"
123
+ )
111
124
  self.E_P = np.nan
112
125
  self.E_F = np.nan
113
126
 
114
127
  # Case 3: Both temperatures at/below ambient
115
- elif self.inl[0]['T'] <= T0 and self.outl[0]['T'] <= T0:
128
+ elif self.inl[0]["T"] <= T0 and self.outl[0]["T"] <= T0:
116
129
  if split_physical_exergy:
117
- self.E_P = abs(self.P) + (
118
- self._total_outlet('m', 'e_T') - self.inl[0]['m'] * self.inl[0]['e_T'])
119
- self.E_F = (self.inl[0]['m'] * self.inl[0]['e_M'] -
120
- self._total_outlet('m', 'e_M'))
130
+ self.E_P = abs(self.P) + (self._total_outlet("m", "e_T") - self.inl[0]["m"] * self.inl[0]["e_T"])
131
+ self.E_F = self.inl[0]["m"] * self.inl[0]["e_M"] - self._total_outlet("m", "e_M")
121
132
  else:
122
- logging.warning("While dealing with expander below ambient, "
123
- "physical exergy should be split into thermal and mechanical components!")
133
+ logging.warning(
134
+ "While dealing with expander below ambient, "
135
+ "physical exergy should be split into thermal and mechanical components!"
136
+ )
124
137
  self.E_P = np.nan
125
138
  self.E_F = np.nan
126
139
  # Invalid case: outlet temperature larger than inlet
127
140
  else:
128
141
  logging.warning(
129
- 'Exergy balance of a turbine where outlet temperature is larger '
130
- 'than inlet temperature is not implemented.'
142
+ "Exergy balance of a turbine where outlet temperature is larger "
143
+ "than inlet temperature is not implemented."
131
144
  )
132
145
  self.E_P = np.nan
133
146
  self.E_F = np.nan
134
147
 
135
148
  # Calculate exergy destruction and efficiency
136
149
  self.E_D = self.E_F - self.E_P
137
- if self.E_F == np.nan:
138
- self.E_D = self.inl[0]['m'] * self.inl[0]['e_PH'] - self._total_outlet('m', 'e_PH') - abs(self.P)
150
+ if np.nan == self.E_F:
151
+ self.E_D = self.inl[0]["m"] * self.inl[0]["e_PH"] - self._total_outlet("m", "e_PH") - abs(self.P)
139
152
  self.epsilon = self.calc_epsilon()
140
153
 
141
154
  # Log the results
142
155
  logging.info(
143
- f"Turbine exergy balance calculated: "
156
+ f"Exergy balance of Turbine {self.name} calculated: "
144
157
  f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
145
158
  f"Efficiency={self.epsilon:.2%}"
146
159
  )
@@ -163,34 +176,34 @@ class Turbine(Component):
163
176
  """
164
177
  total = 0.0
165
178
  for outlet in self.outl.values():
166
- if outlet and mass_flow in outlet and property_name in outlet:
179
+ # Skip power connections; treat missing "kind" as material for backward compatibility
180
+ if outlet and outlet.get("kind", "material") != "power" and mass_flow in outlet and property_name in outlet:
167
181
  total += outlet[mass_flow] * outlet[property_name]
168
182
  return total
169
-
170
183
 
171
184
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
172
185
  """
173
186
  Auxiliary equations for the turbine.
174
-
187
+
175
188
  This function adds rows to the cost matrix A and the right-hand-side vector b to enforce
176
189
  the following auxiliary cost relations:
177
-
190
+
178
191
  For each material outlet (when inlet and first outlet are above ambient temperature T0):
179
-
192
+
180
193
  (1) 1/E_T_in * C_T_in - 1/E_T_out * C_T_out = 0
181
194
  - F-principle: specific thermal exergy costs equalized between inlet and each outlet
182
-
195
+
183
196
  (2) 1/E_M_in * C_M_in - 1/E_M_out * C_M_out = 0
184
197
  - F-principle: specific mechanical exergy costs equalized between inlet and each outlet
185
-
198
+
186
199
  (3) 1/E_CH_in * C_CH_in - 1/E_CH_out * C_CH_out = 0 (if chemical_exergy_enabled)
187
200
  - F-principle: specific chemical exergy costs equalized between inlet and each outlet
188
-
201
+
189
202
  For power outlets (with both source and target components):
190
-
203
+
191
204
  (4) 1/E_ref * C_ref - 1/E_out * C_out = 0
192
205
  - P-principle: specific power exergy costs equalized across all power outlets
193
-
206
+
194
207
  Parameters
195
208
  ----------
196
209
  A : numpy.ndarray
@@ -205,7 +218,7 @@ class Turbine(Component):
205
218
  Data structure for storing equation labels.
206
219
  chemical_exergy_enabled : bool
207
220
  Flag indicating whether chemical exergy auxiliary equations should be added.
208
-
221
+
209
222
  Returns
210
223
  -------
211
224
  A : numpy.ndarray
@@ -231,10 +244,12 @@ class Turbine(Component):
231
244
  A[counter + row_offset, self.inl[0]["CostVar_index"]["T"]] = (
232
245
  1 / self.inl[0]["E_T"] if self.inl[0]["e_T"] != 0 else 1
233
246
  )
234
- A[counter + row_offset, outlet["CostVar_index"]["T"]] = (
235
- -1 / outlet["E_T"] if outlet["e_T"] != 0 else -1
236
- )
237
- equations[counter + row_offset] = f"aux_f_rule_{outlet['name']}"
247
+ A[counter + row_offset, outlet["CostVar_index"]["T"]] = -1 / outlet["E_T"] if outlet["e_T"] != 0 else -1
248
+ equations[counter + row_offset] = {
249
+ "kind": "aux_f_rule",
250
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
251
+ "property": "c_T",
252
+ }
238
253
 
239
254
  # --- Mechanical exergy equation ---
240
255
  A[counter + row_offset + 1, self.inl[0]["CostVar_index"]["M"]] = (
@@ -243,7 +258,11 @@ class Turbine(Component):
243
258
  A[counter + row_offset + 1, outlet["CostVar_index"]["M"]] = (
244
259
  -1 / outlet["E_M"] if outlet["e_M"] != 0 else -1
245
260
  )
246
- equations[counter + row_offset + 1] = f"aux_f_rule_{outlet['name']}"
261
+ equations[counter + row_offset + 1] = {
262
+ "kind": "aux_f_rule",
263
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
264
+ "property": "c_M",
265
+ }
247
266
 
248
267
  # --- Chemical exergy equation (conditionally added) ---
249
268
  if chemical_exergy_enabled:
@@ -253,8 +272,12 @@ class Turbine(Component):
253
272
  A[counter + row_offset + 2, outlet["CostVar_index"]["CH"]] = (
254
273
  -1 / outlet["E_CH"] if outlet["e_CH"] != 0 else -1
255
274
  )
256
- equations[counter + row_offset + 2] = f"aux_f_rule_{outlet['name']}"
257
-
275
+ equations[counter + row_offset + 2] = {
276
+ "kind": "aux_equality",
277
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
278
+ "property": "c_CH",
279
+ }
280
+
258
281
  # Update counter based on number of rows added for all material outlets.
259
282
  num_material_rows = num_rows_per_outlet * len(material_outlets)
260
283
  for j in range(num_material_rows):
@@ -262,10 +285,13 @@ class Turbine(Component):
262
285
  counter += num_material_rows
263
286
  else:
264
287
  logging.warning("Turbine with outlet below T0 not implemented in exergoeconomics yet!")
265
-
288
+
266
289
  # --- Auxiliary equation for shaft power equality ---
267
- power_outlets = [outlet for outlet in self.outl.values()
268
- if outlet.get("kind") == "power" and outlet.get("source_component") and outlet.get("target_component")]
290
+ power_outlets = [
291
+ outlet
292
+ for outlet in self.outl.values()
293
+ if outlet.get("kind") == "power" and outlet.get("source_component") and outlet.get("target_component")
294
+ ]
269
295
  if len(power_outlets) > 1:
270
296
  ref = power_outlets[0]
271
297
  ref_idx = ref["CostVar_index"]["exergy"]
@@ -274,44 +300,124 @@ class Turbine(Component):
274
300
  A[counter, ref_idx] = 1 / ref["E"] if ref["E"] != 0 else 1
275
301
  A[counter, cur_idx] = -1 / outlet["E"] if outlet["E"] != 0 else -1
276
302
  b[counter] = 0
277
- equations[counter] = f"aux_p_rule_power_{self.name}_{outlet['name']}"
303
+ equations[counter] = {
304
+ "kind": "aux_p_rule",
305
+ "objects": [self.name],
306
+ "property": "c_TOT (more power flows)",
307
+ }
278
308
  counter += 1
279
309
 
280
310
  return A, b, counter, equations
281
311
 
282
- def exergoeconomic_balance(self, T0):
283
- """
284
- Perform exergoeconomic balance calculations for the turbine.
312
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
313
+ r"""
314
+ Perform exergoeconomic cost balance for the turbine.
315
+
316
+ The general exergoeconomic balance equation is:
317
+
318
+ .. math::
319
+ \dot{C}^{\mathrm{T}}_{\mathrm{in}}
320
+ + \dot{C}^{\mathrm{M}}_{\mathrm{in}}
321
+ - \dot{C}^{\mathrm{T}}_{\mathrm{out}}
322
+ - \dot{C}^{\mathrm{M}}_{\mathrm{out}}
323
+ + \dot{Z}
324
+ = 0
285
325
 
286
- The turbine may have multiple power outputs and multiple material outputs. In this
287
- function the cost of power is computed as the sum of C_TOT from all inlet streams of kind "power".
288
- Material outlet costs are summed over all outlets of kind "material". The cost balance is then
289
- computed according to the following cases:
326
+ In case the chemical exergy of the streams is known:
290
327
 
291
- Case 1 (both inlet and first outlet above T0):
292
- C_P = (total power cost)
293
- C_F = C_PH_inlet - (sum of C_PH from material outlets)
328
+ .. math::
329
+ \dot{C}^{\mathrm{CH}}_{\mathrm{in}} =
330
+ \dot{C}^{\mathrm{CH}}_{\mathrm{out}}
294
331
 
295
- Case 2 (inlet above T0, outlet at or below T0):
296
- C_P = (total power cost) + (sum of C_T from material outlets)
297
- C_F = C_T_inlet + (C_M_inlet - (sum of C_M from material outlets))
332
+ This method computes cost rates for product and fuel, and derives
333
+ exergoeconomic indicators. The turbine may have multiple power outputs
334
+ and multiple material outlets. The product cost includes the total cost
335
+ of all power streams, while material outlet costs are summed accordingly.
298
336
 
299
- Case 3 (both inlet and outlet at or below T0):
300
- C_P = (total power cost) + ((sum of C_T from material outlets) - C_T_inlet)
301
- C_F = C_M_inlet - (sum of C_M from material outlets)
337
+ **Case 1: Inlet and outlet above ambient temperature**
302
338
 
303
- Finally, the specific fuel cost (c_F), specific product cost (c_P), total cost destruction (C_D),
304
- relative difference (r), and exergoeconomic factor (f) are calculated.
339
+ Both inlet and first material outlet satisfy :math:`T \geq T_0`:
340
+
341
+ .. math::
342
+ \dot{C}_{\mathrm{P}}
343
+ = \sum \dot{C}^{\mathrm{TOT}}_{\mathrm{power,out}}
344
+
345
+ .. math::
346
+ \dot{C}_{\mathrm{F}}
347
+ = \dot{C}^{\mathrm{PH}}_{\mathrm{in}}
348
+ - \sum \dot{C}^{\mathrm{PH}}_{\mathrm{material,out}}
349
+
350
+ **Case 2: Inlet above and outlet at or below ambient temperature**
351
+
352
+ Inlet satisfies :math:`T > T_0` and first material outlet :math:`T \leq T_0`:
353
+
354
+ .. math::
355
+ \dot{C}_{\mathrm{P}}
356
+ = \sum \dot{C}^{\mathrm{TOT}}_{\mathrm{power,out}}
357
+ + \sum \dot{C}^{\mathrm{T}}_{\mathrm{material,out}}
358
+
359
+ .. math::
360
+ \dot{C}_{\mathrm{F}}
361
+ = \dot{C}^{\mathrm{T}}_{\mathrm{in}}
362
+ + \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{in}}
363
+ - \sum \dot{C}^{\mathrm{M}}_{\mathrm{material,out}}\bigr)
364
+
365
+ **Case 3: Both inlet and outlet at or below ambient temperature**
366
+
367
+ Both inlet and first material outlet satisfy :math:`T \leq T_0`:
368
+
369
+ .. math::
370
+ \dot{C}_{\mathrm{P}}
371
+ = \sum \dot{C}^{\mathrm{TOT}}_{\mathrm{power,out}}
372
+ + \bigl(\sum \dot{C}^{\mathrm{T}}_{\mathrm{material,out}}
373
+ - \dot{C}^{\mathrm{T}}_{\mathrm{in}}\bigr)
374
+
375
+ .. math::
376
+ \dot{C}_{\mathrm{F}}
377
+ = \dot{C}^{\mathrm{M}}_{\mathrm{in}}
378
+ - \sum \dot{C}^{\mathrm{M}}_{\mathrm{material,out}}
379
+
380
+ **Calculated exergoeconomic indicators:**
381
+
382
+ .. math::
383
+ c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
384
+
385
+ .. math::
386
+ c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
387
+
388
+ .. math::
389
+ \dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
390
+
391
+ .. math::
392
+ r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
393
+
394
+ .. math::
395
+ f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
305
396
 
306
397
  Parameters
307
398
  ----------
308
399
  T0 : float
309
- Ambient temperature.
310
-
311
- Raises
312
- ------
313
- ValueError
314
- If required cost values are missing.
400
+ Ambient temperature (K).
401
+ chemical_exergy_enabled : bool, optional
402
+ If True, chemical exergy is considered in the calculations.
403
+ Default is False.
404
+
405
+ Attributes Set
406
+ --------------
407
+ C_P : float
408
+ Cost rate of product (currency/time).
409
+ C_F : float
410
+ Cost rate of fuel (currency/time).
411
+ c_P : float
412
+ Specific cost of product (currency/energy).
413
+ c_F : float
414
+ Specific cost of fuel (currency/energy).
415
+ C_D : float
416
+ Cost rate of exergy destruction (currency/time).
417
+ r : float
418
+ Relative cost difference (dimensionless).
419
+ f : float
420
+ Exergoeconomic factor (dimensionless).
315
421
  """
316
422
  # Sum the cost of all outlet power streams.
317
423
  C_power_out = sum(stream.get("C_TOT", 0) for stream in self.outl.values() if stream.get("kind") == "power")
@@ -339,7 +445,9 @@ class Turbine(Component):
339
445
  self.C_F = inlet.get("C_M", 0) - sum_C_M_out
340
446
 
341
447
  else:
342
- logging.warning("Exergoeconomic balance of a turbine with outlet temperature larger than inlet is not implemented.")
448
+ logging.warning(
449
+ "Exergoeconomic balance of a turbine with outlet temperature larger than inlet is not implemented."
450
+ )
343
451
  self.C_P = np.nan
344
452
  self.C_F = np.nan
345
453