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
@@ -1,7 +1,8 @@
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
@@ -10,8 +11,8 @@ class Condenser(Component):
10
11
  Class for exergy and exergoeconomic analysis of condensers (only dissipative).
11
12
 
12
13
  This class performs exergy and exergoeconomic analysis calculations for condenser components,
13
- accounting for two inlet and two outlet streams. This class should be used only for dissipative
14
- condensers. For non-dissipative condensers, use components that are modeled in ExerPy using the
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
15
16
  `HeatExchanger` class.
16
17
 
17
18
  Attributes
@@ -52,7 +53,7 @@ class Condenser(Component):
52
53
  - Z_costs (float): investment cost rate in currency/h, default 0.0
53
54
  """
54
55
  super().__init__(**kwargs)
55
-
56
+
56
57
  def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
57
58
  r"""
58
59
  Compute the exergy balance of the condenser.
@@ -74,8 +75,8 @@ class Condenser(Component):
74
75
  - \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
75
76
  - \dot{E}_{\mathrm{L}}
76
77
 
77
- However, these value can only be accessed via the attributes `E_L` and `E_D` of the component.
78
- In the table of final results of the exergy analysis of the system, the exergy destruction of
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
79
80
  the condenser is counted as the exergy loss and the exergetic destruction due to heat transfer.
80
81
 
81
82
  Parameters
@@ -95,21 +96,24 @@ class Condenser(Component):
95
96
  # Ensure that the component has both inlet and outlet streams
96
97
  if len(self.inl) < 2 or len(self.outl) < 2:
97
98
  raise ValueError("Condenser requires two inlets and two outlets.")
98
-
99
+
99
100
  # Calculate exergy loss (E_L) for the heat transfer process
100
- 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"])
101
102
 
102
103
  # Calculate exergy destruction (E_D)
103
- 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
104
105
 
105
106
  # Exergy fuel and product are not typically defined for a condenser
106
- self.E_F = None
107
- self.E_P = None
108
- self.epsilon = None
109
-
110
- # Log the exergy balance results
111
- 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
112
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
+ )
113
117
 
114
118
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
115
119
  r"""
@@ -162,7 +166,7 @@ class Condenser(Component):
162
166
  + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
163
167
  = 0
164
168
 
165
- Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
169
+ Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
166
170
 
167
171
  The dissipative is not handeld here!
168
172
 
@@ -213,6 +217,7 @@ class Condenser(Component):
213
217
  ValueError
214
218
  If required cost variable indices are missing.
215
219
  """
220
+
216
221
  # Equality equation for mechanical and chemical exergy costs.
217
222
  def set_equal(A, row, in_item, out_item, var):
218
223
  if in_item["e_" + var] != 0 and out_item["e_" + var] != 0:
@@ -269,65 +274,101 @@ class Condenser(Component):
269
274
  # Case 1: All temperatures > T0.
270
275
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
271
276
  set_thermal_f_hot(A, counter + 0)
272
- 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
+ }
273
282
  # Case 2: All temperatures <= T0.
274
283
  elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
275
284
  set_thermal_f_cold(A, counter + 0)
276
- equations[counter] = f"aux_f_rule_cold_{self.name}"
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
+ }
277
290
  logging.warning(
278
- f"All temperatures in {self.name} are below ambient temperature. " \
291
+ f"All temperatures in {self.name} are below ambient temperature. "
279
292
  "This is not a typical case for a dissipative condenser."
280
293
  )
281
294
  # Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= T0)
282
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
283
- self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= 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:
284
296
  set_thermal_p_rule(A, counter + 0)
285
- equations[counter] = f"aux_p_rule_{self.name}"
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
+ }
286
302
  logging.warning(
287
- f"Hot inlet and cold outlet in {self.name} are above ambient temperature, " \
288
- "while hot outlet and cold inlet are below. This is not a typical case for a dissipative condenser." \
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."
289
305
  "The exergoeconomic analysis is counting the outlets as products."
290
306
  )
291
307
  # Case 4: Only hot inlet > T0
292
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
293
- self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= 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:
294
309
  set_thermal_f_cold(A, counter + 0)
295
- equations[counter] = f"aux_f_rule_cold_{self.name}"
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
+ }
296
315
  logging.warning(
297
- f"Cold inlet in {self.name} is below ambient temperature. " \
316
+ f"Cold inlet in {self.name} is below ambient temperature. "
298
317
  "This is not a typical case for a dissipative condenser."
299
318
  )
300
319
  # Case 5: Only cold inlet <= T0
301
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
302
- self.outl[0]["T"] > T0 and self.outl[1]["T"] > 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:
303
321
  set_thermal_f_hot(A, counter + 0)
304
- equations[counter] = f"aux_f_rule_hot_{self.name}"
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
+ }
305
327
  logging.warning(
306
- f"Cold inlet in {self.name} is below ambient temperature. " \
328
+ f"Cold inlet in {self.name} is below ambient temperature. "
307
329
  "This is not a typical case for a dissipative condenser."
308
330
  )
309
331
  # Case 6: hot stream always above T0, cold stream always below T0
310
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
311
- self.outl[0]["T"] > T0 and self.outl[1]["T"] <= 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:
312
333
  print("you shouldn't see this")
313
334
  return
314
335
  # Case 7: Default case.
315
336
  else:
316
337
  set_thermal_f_hot(A, counter + 0)
317
- equations[counter] = f"aux_f_rule_hot_{self.name}"
318
-
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
+
319
344
  # Mechanical equations (always added)
320
345
  set_equal(A, counter + 1, self.inl[0], self.outl[0], "M")
321
346
  set_equal(A, counter + 2, self.inl[1], self.outl[1], "M")
322
- equations[counter + 1] = f"aux_equality_mech_{self.outl[0]['name']}"
323
- equations[counter + 2] = f"aux_equality_mech_{self.outl[1]['name']}"
324
-
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
+
325
358
  # Only add chemical auxiliary equations if chemical exergy is enabled.
326
359
  if chemical_exergy_enabled:
327
360
  set_equal(A, counter + 3, self.inl[0], self.outl[0], "CH")
328
361
  set_equal(A, counter + 4, self.inl[1], self.outl[1], "CH")
329
- equations[counter + 3] = f"aux_equality_chem_{self.outl[0]['name']}"
330
- 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
+ }
331
372
  num_aux_eqs = 5
332
373
  else:
333
374
  # Skip chemical auxiliary equations.
@@ -337,13 +378,13 @@ class Condenser(Component):
337
378
  b[counter + i] = 0
338
379
 
339
380
  return A, b, counter + num_aux_eqs, equations
340
-
341
- def exergoeconomic_balance(self, T0):
381
+
382
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
342
383
  r"""
343
384
  Perform exergoeconomic cost balance for the condenser.
344
385
 
345
386
  Even though this class should only consider dissipative condensers, the exergoeconomic balance is
346
- still performed to ensure consistency. Please note that same of the following cases cases are not
387
+ still performed to ensure consistency. Please note that same of the following cases cases are not
347
388
  typical for dissipative condensers. This may change in a future version of ExerPy.
348
389
 
349
390
  .. math::
@@ -438,34 +479,29 @@ class Condenser(Component):
438
479
  - \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
439
480
  - \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
440
481
  + \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
441
-
482
+
442
483
  Parameters
443
484
  ----------
444
485
  T0 : float
445
486
  Ambient temperature (K).
487
+ chemical_exergy_enabled : bool, optional
488
+ If True, chemical exergy is considered in the calculations.
446
489
  """
447
490
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
448
491
  self.C_P = self.outl[1]["C_T"] - self.inl[1]["C_T"]
449
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
450
- 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"])
451
493
  elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
452
494
  self.C_P = self.outl[0]["C_T"] - self.inl[0]["C_T"]
453
- self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (
454
- self.inl[0]["C_M"] - self.outl[0]["C_M"])
455
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
456
- 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:
457
497
  self.C_P = self.outl[0]["C_T"] + self.outl[1]["C_T"]
458
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
459
- self.outl[0]["C_M"] + self.outl[1]["C_M"])
460
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
461
- 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:
462
500
  self.C_P = self.outl[0]["C_T"]
463
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
464
- 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"])
465
502
  else:
466
503
  self.C_P = self.outl[1]["C_T"]
467
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
468
- 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"])
469
505
 
470
506
  self.c_F = self.C_F / self.E_F
471
507
  self.c_P = self.C_P / self.E_P