exerpy 0.0.2__py3-none-any.whl → 0.0.4__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 (48) hide show
  1. exerpy/__init__.py +2 -4
  2. exerpy/analyses.py +849 -304
  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 +188 -121
  7. exerpy/components/heat_exchanger/condenser.py +98 -62
  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 +214 -68
  22. exerpy/components/turbomachinery/pump.py +215 -68
  23. exerpy/components/turbomachinery/turbine.py +182 -74
  24. exerpy/cost_estimation/__init__.py +65 -0
  25. exerpy/cost_estimation/turton.py +1260 -0
  26. exerpy/data/cost_correlations/cepci_index.json +135 -0
  27. exerpy/data/cost_correlations/component_mapping.json +450 -0
  28. exerpy/data/cost_correlations/material_factors.json +428 -0
  29. exerpy/data/cost_correlations/pressure_factors.json +206 -0
  30. exerpy/data/cost_correlations/turton2008.json +726 -0
  31. exerpy/data/cost_correlations/turton2008_design_analysis_synthesis_components_tables.pdf +0 -0
  32. exerpy/data/cost_correlations/turton2008_design_analysis_synthesis_components_theory.pdf +0 -0
  33. exerpy/functions.py +389 -264
  34. exerpy/parser/from_aspen/aspen_config.py +57 -48
  35. exerpy/parser/from_aspen/aspen_parser.py +373 -280
  36. exerpy/parser/from_ebsilon/__init__.py +2 -2
  37. exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
  38. exerpy/parser/from_ebsilon/ebsilon_config.py +328 -226
  39. exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
  40. exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
  41. exerpy/parser/from_ebsilon/utils.py +16 -11
  42. exerpy/parser/from_tespy/tespy_config.py +33 -1
  43. exerpy/parser/from_tespy/tespy_parser.py +151 -0
  44. {exerpy-0.0.2.dist-info → exerpy-0.0.4.dist-info}/METADATA +43 -2
  45. exerpy-0.0.4.dist-info/RECORD +57 -0
  46. exerpy-0.0.2.dist-info/RECORD +0 -44
  47. {exerpy-0.0.2.dist-info → exerpy-0.0.4.dist-info}/WHEEL +0 -0
  48. {exerpy-0.0.2.dist-info → exerpy-0.0.4.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
@@ -187,7 +186,7 @@ class HeatExchanger(Component):
187
186
  Case 5: Only the cold inlet below ambient temperature
188
187
 
189
188
  If `split_physical_exergy=True`:
190
-
189
+
191
190
  .. math::
192
191
  \dot{E}_{\mathrm{P}}
193
192
  = \dot{E}^{\mathrm{T}}_{\mathrm{out},2}
@@ -198,7 +197,7 @@ class HeatExchanger(Component):
198
197
  - \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
199
198
  + \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
200
199
  - \dot{E}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
201
-
200
+
202
201
  Else:
203
202
 
204
203
  .. math::
@@ -211,7 +210,7 @@ class HeatExchanger(Component):
211
210
  - \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
212
211
  + \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
213
212
 
214
- Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
213
+ Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
215
214
 
216
215
  .. math::
217
216
  \dot{E}_{\mathrm{P}} = \mathrm{NaN}
@@ -258,89 +257,125 @@ class HeatExchanger(Component):
258
257
 
259
258
  if not self.dissipative:
260
259
  # Case 1: All streams are above the ambient temperature
261
- if all([stream['T'] >= T0 for stream in all_streams]):
260
+ if all([stream["T"] >= T0 for stream in all_streams]):
262
261
  if split_physical_exergy:
263
- self.E_P = self.outl[1]['m'] * self.outl[1]['e_T'] - self.inl[1]['m'] * self.inl[1]['e_T']
264
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] - self.outl[0]['m'] * self.outl[0]['e_PH'] + (
265
- self.inl[1]['m'] * self.inl[1]['e_M'] - self.outl[1]['m'] * self.outl[1]['e_M'])
262
+ self.E_P = self.outl[1]["m"] * self.outl[1]["e_T"] - self.inl[1]["m"] * self.inl[1]["e_T"]
263
+ self.E_F = (
264
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
265
+ - self.outl[0]["m"] * self.outl[0]["e_PH"]
266
+ + (self.inl[1]["m"] * self.inl[1]["e_M"] - self.outl[1]["m"] * self.outl[1]["e_M"])
267
+ )
266
268
  else:
267
- self.E_P = self.outl[1]['m'] * self.outl[1]['e_PH'] - self.inl[1]['m'] * self.inl[1]['e_PH']
268
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] - self.outl[0]['m'] * self.outl[0]['e_PH']
269
+ self.E_P = self.outl[1]["m"] * self.outl[1]["e_PH"] - self.inl[1]["m"] * self.inl[1]["e_PH"]
270
+ self.E_F = self.inl[0]["m"] * self.inl[0]["e_PH"] - self.outl[0]["m"] * self.outl[0]["e_PH"]
269
271
 
270
272
  # Case 2: All streams are below or equal to the ambient temperature
271
- elif all([stream['T'] <= T0 for stream in all_streams]):
273
+ elif all([stream["T"] <= T0 for stream in all_streams]):
272
274
  if split_physical_exergy:
273
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_T'] - self.inl[0]['m'] * self.inl[0]['e_T']
274
- self.E_F = self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_PH'] + (
275
- self.inl[0]['m'] * self.inl[0]['e_M'] - self.outl[0]['m'] * self.outl[0]['e_M'])
275
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_T"] - self.inl[0]["m"] * self.inl[0]["e_T"]
276
+ self.E_F = (
277
+ self.inl[1]["m"] * self.inl[1]["e_PH"]
278
+ - self.outl[1]["m"] * self.outl[1]["e_PH"]
279
+ + (self.inl[0]["m"] * self.inl[0]["e_M"] - self.outl[0]["m"] * self.outl[0]["e_M"])
280
+ )
276
281
  else:
277
- logging.warning("While dealing with heat exchnager below ambient temperautre, "
278
- "physical exergy should be split into thermal and mechanical components!")
279
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_PH'] - self.inl[0]['m'] * self.inl[0]['e_PH']
280
- self.E_F = self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_PH']
282
+ logging.warning(
283
+ "While dealing with heat exchnager below ambient temperautre, "
284
+ "physical exergy should be split into thermal and mechanical components!"
285
+ )
286
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_PH"] - self.inl[0]["m"] * self.inl[0]["e_PH"]
287
+ self.E_F = self.inl[1]["m"] * self.inl[1]["e_PH"] - self.outl[1]["m"] * self.outl[1]["e_PH"]
281
288
 
282
289
  # Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= T0)
283
- elif (self.inl[0]['T'] > T0 and self.outl[1]['T'] > T0 and
284
- self.outl[0]['T'] <= T0 and self.inl[1]['T'] <= T0):
290
+ elif (
291
+ self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0
292
+ ):
285
293
  if split_physical_exergy:
286
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_T'] + self.outl[1]['m'] * self.outl[1]['e_T']
287
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] + self.inl[1]['m'] * self.inl[1]['e_PH'] - (
288
- self.outl[0]['m'] * self.outl[0]['e_M'] + self.outl[1]['m'] * self.outl[1]['e_M'])
294
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_T"] + self.outl[1]["m"] * self.outl[1]["e_T"]
295
+ self.E_F = (
296
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
297
+ + self.inl[1]["m"] * self.inl[1]["e_PH"]
298
+ - (self.outl[0]["m"] * self.outl[0]["e_M"] + self.outl[1]["m"] * self.outl[1]["e_M"])
299
+ )
289
300
  else:
290
- logging.warning("While dealing with heat exchnager below ambient temperautre, "
291
- "physical exergy should be split into thermal and mechanical components!")
292
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_PH'] + self.outl[1]['m'] * self.outl[1]['e_PH']
293
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] + self.inl[1]['m'] * self.inl[1]['e_PH']
301
+ logging.warning(
302
+ "While dealing with heat exchnager below ambient temperautre, "
303
+ "physical exergy should be split into thermal and mechanical components!"
304
+ )
305
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_PH"] + self.outl[1]["m"] * self.outl[1]["e_PH"]
306
+ self.E_F = self.inl[0]["m"] * self.inl[0]["e_PH"] + self.inl[1]["m"] * self.inl[1]["e_PH"]
294
307
 
295
308
  # Case 4: Only hot inlet > T0
296
- elif (self.inl[0]['T'] > T0 and self.inl[1]['T'] <= T0 and
297
- self.outl[0]['T'] <= T0 and self.outl[1]['T'] <= T0):
309
+ elif (
310
+ self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0
311
+ ):
298
312
  if split_physical_exergy:
299
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_T']
300
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] + self.inl[1]['m'] * self.inl[1]['e_PH'] - (
301
- self.outl[1]['m'] * self.outl[1]['e_PH'] + self.outl[0]['m'] * self.outl[0]['e_M'])
313
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_T"]
314
+ self.E_F = (
315
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
316
+ + self.inl[1]["m"] * self.inl[1]["e_PH"]
317
+ - (self.outl[1]["m"] * self.outl[1]["e_PH"] + self.outl[0]["m"] * self.outl[0]["e_M"])
318
+ )
302
319
  else:
303
- logging.warning("While dealing with heat exchnager below ambient temperautre, "
304
- "physical exergy should be split into thermal and mechanical components!")
305
- self.E_P = self.outl[0]['m'] * self.outl[0]['e_PH']
306
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] + (
307
- self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_PH'])
320
+ logging.warning(
321
+ "While dealing with heat exchnager below ambient temperautre, "
322
+ "physical exergy should be split into thermal and mechanical components!"
323
+ )
324
+ self.E_P = self.outl[0]["m"] * self.outl[0]["e_PH"]
325
+ self.E_F = self.inl[0]["m"] * self.inl[0]["e_PH"] + (
326
+ self.inl[1]["m"] * self.inl[1]["e_PH"] - self.outl[1]["m"] * self.outl[1]["e_PH"]
327
+ )
308
328
 
309
329
  # Case 5: Only cold inlet <= 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):
330
+ 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
331
  if split_physical_exergy:
313
- self.E_P = self.outl[1]['m'] * self.outl[1]['e_T']
314
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] - self.outl[0]['m'] * self.outl[0]['e_PH'] + (
315
- self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_M'])
332
+ self.E_P = self.outl[1]["m"] * self.outl[1]["e_T"]
333
+ self.E_F = (
334
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
335
+ - self.outl[0]["m"] * self.outl[0]["e_PH"]
336
+ + (self.inl[1]["m"] * self.inl[1]["e_PH"] - self.outl[1]["m"] * self.outl[1]["e_M"])
337
+ )
316
338
  else:
317
- logging.warning("While dealing with heat exchnager below ambient temperautre, "
318
- "physical exergy should be split into thermal and mechanical components!")
319
- self.E_P = self.outl[1]['m'] * self.outl[1]['e_PH']
320
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] - self.outl[0]['m'] * self.outl[0]['e_PH'] + (
321
- self.inl[1]['m'] * self.inl[1]['e_PH'])
322
-
339
+ logging.warning(
340
+ "While dealing with heat exchnager below ambient temperautre, "
341
+ "physical exergy should be split into thermal and mechanical components!"
342
+ )
343
+ self.E_P = self.outl[1]["m"] * self.outl[1]["e_PH"]
344
+ self.E_F = (
345
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
346
+ - self.outl[0]["m"] * self.outl[0]["e_PH"]
347
+ + (self.inl[1]["m"] * self.inl[1]["e_PH"])
348
+ )
349
+
323
350
  # Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
324
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
325
- self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0):
351
+ elif (
352
+ self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0
353
+ ):
326
354
  self.E_P = np.nan
327
- self.E_F = self.inl[0]['m'] * self.inl[0]['e_PH'] - self.outl[0]['m'] * self.outl[0]['e_PH'] + (
328
- self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_PH'])
329
-
330
- logging.warning(f"Component {self.name} is dissipative. This component should be " \
331
- "handled with the `dissipative` flag set to True.")
332
-
355
+ self.E_F = (
356
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
357
+ - self.outl[0]["m"] * self.outl[0]["e_PH"]
358
+ + (self.inl[1]["m"] * self.inl[1]["e_PH"] - self.outl[1]["m"] * self.outl[1]["e_PH"])
359
+ )
360
+
361
+ logging.warning(
362
+ f"Component {self.name} is dissipative. This component should be "
363
+ "handled with the `dissipative` flag set to True."
364
+ )
365
+
333
366
  # Case 7: Not implemented case
334
- else:
335
- logging.error(f"The heat exchanger {self.name} has an unexpected temperature configuration. "
336
- "Please check the inlet and outlet temperatures.")
367
+ else:
368
+ logging.error(
369
+ f"The heat exchanger {self.name} has an unexpected temperature configuration. "
370
+ "Please check the inlet and outlet temperatures."
371
+ )
337
372
 
338
373
  else:
339
374
  self.E_F = (
340
- self.inl[0]['m'] * self.inl[0]['e_PH']
341
- - self.outl[0]['m'] * self.outl[0]['e_PH']
342
- - self.outl[1]['m'] * self.outl[1]['e_PH']
343
- + self.inl[1]['m'] * self.inl[1]['e_PH']
375
+ self.inl[0]["m"] * self.inl[0]["e_PH"]
376
+ - self.outl[0]["m"] * self.outl[0]["e_PH"]
377
+ - self.outl[1]["m"] * self.outl[1]["e_PH"]
378
+ + self.inl[1]["m"] * self.inl[1]["e_PH"]
344
379
  )
345
380
  self.E_P = np.nan
346
381
  # Calculate exergy destruction and efficiency
@@ -352,12 +387,11 @@ class HeatExchanger(Component):
352
387
 
353
388
  # Log the results
354
389
  logging.info(
355
- f"HeatExchanger exergy balance calculated: "
390
+ f"Exergy balance of HeatExchanger {self.name} calculated: "
356
391
  f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
357
392
  f"Efficiency={self.epsilon:.2%}"
358
393
  )
359
394
 
360
-
361
395
  def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
362
396
  r"""
363
397
  Add auxiliary cost equations for the heat exchanger.
@@ -409,7 +443,7 @@ class HeatExchanger(Component):
409
443
  + \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
410
444
  = 0
411
445
 
412
- Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
446
+ Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
413
447
 
414
448
  The dissipative is not handeld here!
415
449
 
@@ -460,11 +494,12 @@ class HeatExchanger(Component):
460
494
  ValueError
461
495
  If required cost variable indices are missing.
462
496
  """
497
+
463
498
  # Equality equation for mechanical and chemical exergy costs.
464
499
  def set_equal(A, row, in_item, out_item, var):
465
500
  if in_item["e_" + var] != 0 and out_item["e_" + var] != 0:
466
- A[row, in_item["CostVar_index"][var]] = 1 / in_item["e_" + var]
467
- A[row, out_item["CostVar_index"][var]] = -1 / out_item["e_" + var]
501
+ A[row, in_item["CostVar_index"][var]] = 1 / in_item["E_" + var]
502
+ A[row, out_item["CostVar_index"][var]] = -1 / out_item["E_" + var]
468
503
  elif in_item["e_" + var] == 0 and out_item["e_" + var] != 0:
469
504
  A[row, in_item["CostVar_index"][var]] = 1
470
505
  elif in_item["e_" + var] != 0 and out_item["e_" + var] == 0:
@@ -516,49 +551,85 @@ class HeatExchanger(Component):
516
551
  # Case 1: All temperatures > T0.
517
552
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
518
553
  set_thermal_f_hot(A, counter + 0)
519
- equations[counter] = f"aux_f_rule_hot_{self.name}"
554
+ equations[counter] = {
555
+ "kind": "aux_f_rule_hot",
556
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
557
+ "property": "c_T",
558
+ }
520
559
  # Case 2: All temperatures <= T0.
521
560
  elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
522
561
  set_thermal_f_cold(A, counter + 0)
523
- equations[counter] = f"aux_f_rule_cold_{self.name}"
562
+ equations[counter] = {
563
+ "kind": "aux_f_rule_cold",
564
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
565
+ "property": "c_T",
566
+ }
524
567
  # Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= T0)
525
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
526
- self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0):
568
+ elif self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0:
527
569
  set_thermal_p_rule(A, counter + 0)
528
- equations[counter] = f"aux_p_rule_{self.name}"
570
+ equations[counter] = {
571
+ "kind": "aux_p_rule",
572
+ "objects": [self.name, self.outl[0]["name"], self.outl[1]["name"]],
573
+ "property": "c_T",
574
+ }
529
575
  # Case 4: Only hot inlet > T0
530
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
531
- self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0):
576
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0:
532
577
  set_thermal_f_cold(A, counter + 0)
533
- equations[counter] = f"aux_f_rule_cold_{self.name}"
578
+ equations[counter] = {
579
+ "kind": "aux_f_rule_cold",
580
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
581
+ "property": "c_T",
582
+ }
534
583
  # Case 5: Only cold inlet <= T0
535
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
536
- self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0):
584
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0:
537
585
  set_thermal_f_hot(A, counter + 0)
538
- equations[counter] = f"aux_f_rule_hot_{self.name}"
586
+ equations[counter] = {
587
+ "kind": "aux_f_rule_hot",
588
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
589
+ "property": "c_T",
590
+ }
539
591
  # Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
540
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
541
- self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0):
542
- logging.warning(f"Component {self.name} is dissipative. This component should be " \
543
- "handled with the `dissipative` flag set to True.")
592
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0:
593
+ logging.warning(
594
+ f"Component {self.name} is dissipative. This component should be "
595
+ "handled with the `dissipative` flag set to True."
596
+ )
544
597
  return
545
598
  # Case 7: Not implemented case
546
- else:
547
- logging.error(f"The heat exchanger {self.name} has an unexpected temperature configuration. "
548
- "Please check the inlet and outlet temperatures.")
549
-
599
+ else:
600
+ logging.error(
601
+ f"The heat exchanger {self.name} has an unexpected temperature configuration. "
602
+ "Please check the inlet and outlet temperatures."
603
+ )
604
+
550
605
  # Mechanical equations (always added)
551
606
  set_equal(A, counter + 1, self.inl[0], self.outl[0], "M")
552
607
  set_equal(A, counter + 2, self.inl[1], self.outl[1], "M")
553
- equations[counter + 1] = f"aux_equality_mech_{self.outl[0]['name']}"
554
- equations[counter + 2] = f"aux_equality_mech_{self.outl[1]['name']}"
555
-
608
+ equations[counter + 1] = {
609
+ "kind": "aux_equality",
610
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
611
+ "property": "c_M",
612
+ }
613
+ equations[counter + 2] = {
614
+ "kind": "aux_equality",
615
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
616
+ "property": "c_M",
617
+ }
618
+
556
619
  # Only add chemical auxiliary equations if chemical exergy is enabled.
557
620
  if chemical_exergy_enabled:
558
621
  set_equal(A, counter + 3, self.inl[0], self.outl[0], "CH")
559
622
  set_equal(A, counter + 4, self.inl[1], self.outl[1], "CH")
560
- equations[counter + 3] = f"aux_equality_chem_{self.outl[0]['name']}"
561
- equations[counter + 4] = f"aux_equality_chem_{self.outl[1]['name']}"
623
+ equations[counter + 3] = {
624
+ "kind": "aux_equality",
625
+ "objects": [self.name, self.inl[0]["name"], self.outl[0]["name"]],
626
+ "property": "c_CH",
627
+ }
628
+ equations[counter + 4] = {
629
+ "kind": "aux_equality",
630
+ "objects": [self.name, self.inl[1]["name"], self.outl[1]["name"]],
631
+ "property": "c_M",
632
+ }
562
633
  num_aux_eqs = 5
563
634
  else:
564
635
  # Skip chemical auxiliary equations.
@@ -569,7 +640,7 @@ class HeatExchanger(Component):
569
640
 
570
641
  return A, b, counter + num_aux_eqs, equations
571
642
 
572
- def exergoeconomic_balance(self, T0):
643
+ def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
573
644
  r"""
574
645
  Perform exergoeconomic cost balance for the heat exchanger.
575
646
 
@@ -665,55 +736,51 @@ class HeatExchanger(Component):
665
736
  - \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
666
737
  - \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
667
738
  + \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
668
-
739
+
669
740
  Parameters
670
741
  ----------
671
742
  T0 : float
672
743
  Ambient temperature (K).
744
+ chemical_exergy_enabled : bool, optional
745
+ If True, chemical exergy is considered in the calculations.
673
746
  """
674
747
  # Case 1: All streams are above the ambient temperature
675
748
  if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
676
749
  self.C_P = self.outl[1]["C_T"] - self.inl[1]["C_T"]
677
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
678
- self.inl[1]["C_M"] - self.outl[1]["C_M"])
750
+ self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_M"] - self.outl[1]["C_M"])
679
751
  # Case 2: All streams are below or equal to the ambient temperature
680
752
  elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
681
753
  self.C_P = self.outl[0]["C_T"] - self.inl[0]["C_T"]
682
- self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (
683
- self.inl[0]["C_M"] - self.outl[0]["C_M"])
754
+ self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (self.inl[0]["C_M"] - self.outl[0]["C_M"])
684
755
  # Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= T0)
685
- elif (self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and
686
- self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0):
756
+ elif self.inl[0]["T"] > T0 and self.outl[1]["T"] > T0 and self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0:
687
757
  self.C_P = self.outl[0]["C_T"] + self.outl[1]["C_T"]
688
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
689
- self.outl[0]["C_M"] + self.outl[1]["C_M"])
758
+ self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[0]["C_M"] + self.outl[1]["C_M"])
690
759
  # Case 4: Only hot inlet > T0
691
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
692
- self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0):
760
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0:
693
761
  self.C_P = self.outl[0]["C_T"]
694
- self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
695
- self.outl[1]["C_PH"] + self.outl[0]["C_M"])
762
+ self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[1]["C_PH"] + self.outl[0]["C_M"])
696
763
  # Case 5: Only cold inlet <= T0
697
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
698
- self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0):
764
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] > T0:
699
765
  self.C_P = self.outl[1]["C_T"]
700
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
701
- self.inl[1]["C_PH"] - self.outl[1]["C_M"])
766
+ self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_PH"] - self.outl[1]["C_M"])
702
767
  # Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
703
- elif (self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and
704
- self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0):
705
- logging.warning(f"Component {self.name} is dissipative. This component should be " \
706
- "handled with the `dissipative` flag set to True.")
768
+ elif self.inl[0]["T"] > T0 and self.inl[1]["T"] <= T0 and self.outl[0]["T"] > T0 and self.outl[1]["T"] <= T0:
769
+ logging.warning(
770
+ f"Component {self.name} is dissipative. This component should be "
771
+ "handled with the `dissipative` flag set to True."
772
+ )
707
773
  self.C_P = np.nan
708
- self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
709
- self.inl[1]["C_PH"] - self.outl[1]["C_PH"])
774
+ self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_PH"] - self.outl[1]["C_PH"])
710
775
  # Case 7: Not implemented case
711
- else:
712
- logging.error(f"The heat exchanger {self.name} has an unexpected temperature configuration. "
713
- "Please check the inlet and outlet temperatures.")
776
+ else:
777
+ logging.error(
778
+ f"The heat exchanger {self.name} has an unexpected temperature configuration. "
779
+ "Please check the inlet and outlet temperatures."
780
+ )
714
781
 
715
782
  self.c_F = self.C_F / self.E_F
716
783
  self.c_P = self.C_P / self.E_P
717
784
  self.C_D = self.c_F * self.E_D
718
785
  self.r = (self.c_P - self.c_F) / self.c_F
719
- self.f = self.Z_costs / (self.Z_costs + self.C_D)
786
+ self.f = self.Z_costs / (self.Z_costs + self.C_D)