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.
- exerpy/__init__.py +2 -4
- exerpy/analyses.py +597 -297
- exerpy/components/__init__.py +3 -0
- exerpy/components/combustion/base.py +53 -35
- exerpy/components/component.py +8 -8
- exerpy/components/heat_exchanger/base.py +186 -119
- exerpy/components/heat_exchanger/condenser.py +96 -60
- exerpy/components/heat_exchanger/simple.py +237 -137
- exerpy/components/heat_exchanger/steam_generator.py +46 -41
- exerpy/components/helpers/cycle_closer.py +61 -34
- exerpy/components/helpers/power_bus.py +117 -0
- exerpy/components/nodes/deaerator.py +176 -58
- exerpy/components/nodes/drum.py +50 -39
- exerpy/components/nodes/flash_tank.py +218 -43
- exerpy/components/nodes/mixer.py +249 -69
- exerpy/components/nodes/splitter.py +173 -0
- exerpy/components/nodes/storage.py +130 -0
- exerpy/components/piping/valve.py +311 -115
- exerpy/components/power_machines/generator.py +105 -38
- exerpy/components/power_machines/motor.py +111 -39
- exerpy/components/turbomachinery/compressor.py +181 -63
- exerpy/components/turbomachinery/pump.py +182 -63
- exerpy/components/turbomachinery/turbine.py +182 -74
- exerpy/functions.py +388 -263
- exerpy/parser/from_aspen/aspen_config.py +57 -48
- exerpy/parser/from_aspen/aspen_parser.py +373 -280
- exerpy/parser/from_ebsilon/__init__.py +2 -2
- exerpy/parser/from_ebsilon/check_ebs_path.py +15 -19
- exerpy/parser/from_ebsilon/ebsilon_config.py +328 -226
- exerpy/parser/from_ebsilon/ebsilon_functions.py +205 -38
- exerpy/parser/from_ebsilon/ebsilon_parser.py +392 -255
- exerpy/parser/from_ebsilon/utils.py +16 -11
- exerpy/parser/from_tespy/tespy_config.py +32 -1
- exerpy/parser/from_tespy/tespy_parser.py +151 -0
- {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/METADATA +43 -2
- exerpy-0.0.3.dist-info/RECORD +48 -0
- exerpy-0.0.2.dist-info/RECORD +0 -44
- {exerpy-0.0.2.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
- {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
|
|
@@ -282,7 +281,7 @@ class SimpleHeatExchanger(Component):
|
|
|
282
281
|
If required inlet or outlet are missing.
|
|
283
282
|
"""
|
|
284
283
|
# Validate the number of inlets and outlets
|
|
285
|
-
if not hasattr(self,
|
|
284
|
+
if not hasattr(self, "inl") or not hasattr(self, "outl") or len(self.inl) < 1 or len(self.outl) < 1:
|
|
286
285
|
msg = "SimpleHeatExchanger requires at least one inlet and one outlet as well as one heat flow."
|
|
287
286
|
logging.error(msg)
|
|
288
287
|
raise ValueError(msg)
|
|
@@ -296,7 +295,7 @@ class SimpleHeatExchanger(Component):
|
|
|
296
295
|
outlet = self.outl[0]
|
|
297
296
|
|
|
298
297
|
# Calculate heat transfer Q
|
|
299
|
-
Q = outlet[
|
|
298
|
+
Q = outlet["m"] * outlet["h"] - inlet["m"] * inlet["h"]
|
|
300
299
|
|
|
301
300
|
# Initialize E_P and E_F
|
|
302
301
|
self.E_P = 0.0
|
|
@@ -304,78 +303,87 @@ class SimpleHeatExchanger(Component):
|
|
|
304
303
|
|
|
305
304
|
# Case 1: Heat is released (Q < 0)
|
|
306
305
|
if Q < 0:
|
|
307
|
-
if inlet[
|
|
306
|
+
if inlet["T"] >= T0 and outlet["T"] >= T0:
|
|
308
307
|
if split_physical_exergy:
|
|
309
|
-
self.E_P =
|
|
308
|
+
self.E_P = (
|
|
309
|
+
np.nan if getattr(self, "dissipative", False) else inlet["m"] * (inlet["e_T"] - outlet["e_T"])
|
|
310
|
+
)
|
|
310
311
|
else:
|
|
311
|
-
self.E_P =
|
|
312
|
-
|
|
312
|
+
self.E_P = (
|
|
313
|
+
np.nan if getattr(self, "dissipative", False) else inlet["m"] * (inlet["e_PH"] - outlet["e_PH"])
|
|
314
|
+
)
|
|
315
|
+
self.E_F = inlet["m"] * (inlet["e_PH"] - outlet["e_PH"])
|
|
313
316
|
|
|
314
|
-
elif inlet[
|
|
317
|
+
elif inlet["T"] >= T0 and outlet["T"] < T0:
|
|
315
318
|
if split_physical_exergy:
|
|
316
|
-
self.E_P = outlet[
|
|
317
|
-
self.E_F = (
|
|
318
|
-
|
|
319
|
+
self.E_P = outlet["m"] * outlet["e_T"]
|
|
320
|
+
self.E_F = (
|
|
321
|
+
inlet["m"] * inlet["e_T"]
|
|
322
|
+
+ outlet["m"] * outlet["e_T"]
|
|
323
|
+
+ (inlet["m"] * inlet["e_M"] - outlet["m"] * outlet["e_M"])
|
|
324
|
+
)
|
|
319
325
|
else:
|
|
320
|
-
self.E_P = outlet[
|
|
321
|
-
self.E_F = inlet[
|
|
326
|
+
self.E_P = outlet["m"] * outlet["e_PH"]
|
|
327
|
+
self.E_F = inlet["m"] * inlet["e_PH"]
|
|
322
328
|
|
|
323
|
-
elif inlet[
|
|
329
|
+
elif inlet["T"] <= T0 and outlet["T"] < T0:
|
|
324
330
|
if split_physical_exergy:
|
|
325
|
-
self.E_P = outlet[
|
|
326
|
-
self.E_F = self.E_P + inlet[
|
|
331
|
+
self.E_P = outlet["m"] * (outlet["e_T"] - inlet["e_T"])
|
|
332
|
+
self.E_F = self.E_P + inlet["m"] * (inlet["e_M"] - outlet["m"] * outlet["e_M"])
|
|
327
333
|
else:
|
|
328
|
-
self.E_P =
|
|
329
|
-
|
|
330
|
-
|
|
334
|
+
self.E_P = (
|
|
335
|
+
np.nan
|
|
336
|
+
if getattr(self, "dissipative", False)
|
|
337
|
+
else outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
338
|
+
)
|
|
339
|
+
self.E_F = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
331
340
|
|
|
332
341
|
else:
|
|
333
342
|
# Unimplemented corner case
|
|
334
|
-
logging.warning(
|
|
335
|
-
"SimpleHeatExchanger: unimplemented case (Q < 0, T_in < T0 < T_out?)."
|
|
336
|
-
)
|
|
343
|
+
logging.warning("SimpleHeatExchanger: unimplemented case (Q < 0, T_in < T0 < T_out?).")
|
|
337
344
|
self.E_P = np.nan
|
|
338
345
|
self.E_F = np.nan
|
|
339
346
|
|
|
340
347
|
# Case 2: Heat is added (Q > 0)
|
|
341
348
|
elif Q > 0:
|
|
342
|
-
if inlet[
|
|
349
|
+
if inlet["T"] >= T0 and outlet["T"] >= T0:
|
|
343
350
|
if split_physical_exergy:
|
|
344
|
-
self.E_P = outlet[
|
|
345
|
-
self.E_F = outlet[
|
|
351
|
+
self.E_P = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
352
|
+
self.E_F = outlet["m"] * (outlet["e_T"] - inlet["e_T"])
|
|
346
353
|
else:
|
|
347
|
-
self.E_P = outlet[
|
|
348
|
-
self.E_F = outlet[
|
|
349
|
-
elif inlet[
|
|
354
|
+
self.E_P = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
355
|
+
self.E_F = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
356
|
+
elif inlet["T"] < T0 and outlet["T"] >= T0:
|
|
350
357
|
if split_physical_exergy:
|
|
351
|
-
self.E_P = outlet[
|
|
352
|
-
self.E_F =
|
|
353
|
-
(inlet['m'] * inlet['e_M'] - outlet['m'] * outlet['e_M']))
|
|
358
|
+
self.E_P = outlet["m"] * (outlet["e_T"] + inlet["e_T"])
|
|
359
|
+
self.E_F = inlet["m"] * inlet["e_T"] + (inlet["m"] * inlet["e_M"] - outlet["m"] * outlet["e_M"])
|
|
354
360
|
else:
|
|
355
|
-
self.E_P = outlet[
|
|
356
|
-
self.E_F = outlet[
|
|
361
|
+
self.E_P = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
362
|
+
self.E_F = outlet["m"] * (outlet["e_PH"] - inlet["e_PH"])
|
|
357
363
|
|
|
358
|
-
elif inlet[
|
|
364
|
+
elif inlet["T"] < T0 and outlet["T"] <= T0:
|
|
359
365
|
if split_physical_exergy:
|
|
360
|
-
self.E_P =
|
|
361
|
-
|
|
362
|
-
(
|
|
363
|
-
|
|
366
|
+
self.E_P = (
|
|
367
|
+
np.nan
|
|
368
|
+
if getattr(self, "dissipative", False)
|
|
369
|
+
else inlet["m"] * (inlet["e_T"] - outlet["e_T"])
|
|
370
|
+
+ (outlet["m"] * outlet["e_M"] - inlet["m"] * inlet["e_M"])
|
|
371
|
+
)
|
|
372
|
+
self.E_F = inlet["m"] * (inlet["e_T"] - outlet["e_T"])
|
|
364
373
|
else:
|
|
365
|
-
self.E_P =
|
|
366
|
-
inlet[
|
|
367
|
-
|
|
374
|
+
self.E_P = (
|
|
375
|
+
np.nan if getattr(self, "dissipative", False) else inlet["m"] * (inlet["e_PH"] - outlet["e_PH"])
|
|
376
|
+
)
|
|
377
|
+
self.E_F = inlet["m"] * (inlet["e_PH"] - outlet["e_PH"])
|
|
368
378
|
else:
|
|
369
|
-
logging.warning(
|
|
370
|
-
"SimpleHeatExchanger: unimplemented case (Q > 0, T_in > T0 > T_out?)."
|
|
371
|
-
)
|
|
379
|
+
logging.warning("SimpleHeatExchanger: unimplemented case (Q > 0, T_in > T0 > T_out?).")
|
|
372
380
|
self.E_P = np.nan
|
|
373
381
|
self.E_F = np.nan
|
|
374
382
|
|
|
375
383
|
# Case 3: Fully dissipative or Q == 0
|
|
376
384
|
else:
|
|
377
385
|
self.E_P = np.nan
|
|
378
|
-
self.E_F = inlet[
|
|
386
|
+
self.E_F = inlet["m"] * (inlet["e_PH"] - outlet["e_PH"])
|
|
379
387
|
|
|
380
388
|
# Calculate exergy destruction
|
|
381
389
|
if np.isnan(self.E_P):
|
|
@@ -388,93 +396,112 @@ class SimpleHeatExchanger(Component):
|
|
|
388
396
|
|
|
389
397
|
# Log the results
|
|
390
398
|
logging.info(
|
|
391
|
-
f"SimpleHeatExchanger
|
|
399
|
+
f"Exergy balance of SimpleHeatExchanger {self.name} calculated: "
|
|
392
400
|
f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
|
|
393
401
|
f"Efficiency={self.epsilon:.2%}"
|
|
394
402
|
)
|
|
395
403
|
|
|
396
|
-
|
|
397
404
|
def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
|
|
398
405
|
r"""
|
|
399
406
|
This function must be implemented in the future.
|
|
400
407
|
|
|
401
408
|
The exergoeconomic analysis of SimpleHeatExchanger is not implemented yet.
|
|
402
409
|
"""
|
|
403
|
-
|
|
404
|
-
logging.error(
|
|
405
|
-
"The exergoeconomic analysis of SimpleHeatExchanger is not implemented yet. "
|
|
406
|
-
"This method will be implemented in a future release."
|
|
407
|
-
)
|
|
408
|
-
r"""
|
|
409
|
-
Add auxiliary cost equations for the heat exchanger.
|
|
410
|
-
|
|
411
|
-
For all cases:
|
|
412
|
-
|
|
413
|
-
.. math::
|
|
414
|
-
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out}}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out}}
|
|
415
|
-
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in}}}\,\dot{C}^{\mathrm{Tc}}_{\mathrm{in}}
|
|
416
|
-
= 0
|
|
417
410
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
-\frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{out}}\,\dot{C}^{\mathrm{M}}_{\mathrm{out}}
|
|
422
|
-
+ \frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{in}}\,\dot{C}^{\mathrm{M}}_{\mathrm{in}}
|
|
423
|
-
= 0
|
|
411
|
+
# Extract inlet and outlet
|
|
412
|
+
inlet = self.inl[0]
|
|
413
|
+
outlet = self.outl[0]
|
|
424
414
|
|
|
425
|
-
|
|
415
|
+
# Calculate heat transfer Q
|
|
416
|
+
Q = outlet["m"] * outlet["h"] - inlet["m"] * inlet["h"]
|
|
417
|
+
|
|
418
|
+
# Extract temperatures
|
|
419
|
+
T_in = inlet["T"]
|
|
420
|
+
T_out = outlet["T"]
|
|
421
|
+
|
|
422
|
+
# Equality equation for mechanical exergy costs (c_M,in = c_M,out)
|
|
423
|
+
A[counter, inlet["CostVar_index"]["M"]] = 1 / inlet["E_M"] if inlet["e_M"] != 0 else 1
|
|
424
|
+
A[counter, outlet["CostVar_index"]["M"]] = -1 / outlet["E_M"] if outlet["e_M"] != 0 else -1
|
|
425
|
+
equations[counter] = {
|
|
426
|
+
"kind": "aux_equality",
|
|
427
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
428
|
+
"property": "c_M",
|
|
429
|
+
}
|
|
430
|
+
b[counter] = 0
|
|
431
|
+
counter += 1
|
|
426
432
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
= 0
|
|
433
|
+
# Equality equation for chemical exergy costs (c_CH,in = c_CH,out)
|
|
434
|
+
if chemical_exergy_enabled:
|
|
435
|
+
A[counter, inlet["CostVar_index"]["CH"]] = 1 / inlet["E_CH"] if inlet["e_CH"] != 0 else 1
|
|
436
|
+
A[counter, outlet["CostVar_index"]["CH"]] = -1 / outlet["E_CH"] if outlet["e_CH"] != 0 else -1
|
|
437
|
+
equations[counter] = {
|
|
438
|
+
"kind": "aux_equality",
|
|
439
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
440
|
+
"property": "c_CH",
|
|
441
|
+
}
|
|
442
|
+
b[counter] = 0
|
|
443
|
+
counter += 1
|
|
444
|
+
|
|
445
|
+
# Thermal exergy cost equations
|
|
431
446
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
447
|
+
# Case 1: Heat is released (Q < 0)
|
|
448
|
+
if Q < 0:
|
|
449
|
+
# Case 1.1: Both streams above ambient temperature
|
|
450
|
+
if T_in >= T0 and T_out >= T0:
|
|
451
|
+
# Apply F-rule to thermal exergy (c_T,in = c_T,out)
|
|
452
|
+
A[counter, inlet["CostVar_index"]["T"]] = 1 / inlet["E_T"] if inlet["e_T"] != 0 else 1
|
|
453
|
+
A[counter, outlet["CostVar_index"]["T"]] = -1 / outlet["E_T"] if outlet["e_T"] != 0 else -1
|
|
454
|
+
equations[counter] = {
|
|
455
|
+
"kind": "aux_f_rule",
|
|
456
|
+
"objects": [self.name, inlet["name"], outlet["name"]],
|
|
457
|
+
"property": "c_T",
|
|
458
|
+
}
|
|
459
|
+
b[counter] = 0
|
|
460
|
+
counter += 1
|
|
461
|
+
|
|
462
|
+
elif T_in >= T0 and T_out < T0:
|
|
463
|
+
# Tricky case: inlet above T0, outlet below T0
|
|
464
|
+
logging.warning(
|
|
465
|
+
f"SimpleHeatExchanger '{self.name}': Stream crossing ambient temperature "
|
|
466
|
+
f"during heat release not implemented in exergoeconomics yet!"
|
|
467
|
+
)
|
|
439
468
|
|
|
440
|
-
|
|
469
|
+
else:
|
|
470
|
+
# Tricky case: both streams below T0 while heat is released
|
|
471
|
+
logging.warning(
|
|
472
|
+
f"SimpleHeatExchanger '{self.name}': Both streams below T0 during heat release "
|
|
473
|
+
f"not implemented in exergoeconomics yet!"
|
|
474
|
+
)
|
|
441
475
|
|
|
442
|
-
#
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
476
|
+
# Case 2: Heat is added (Q > 0)
|
|
477
|
+
elif Q > 0:
|
|
478
|
+
# Case 2.1: Both streams below ambient temperature
|
|
479
|
+
if T_in < T0 and T_out < T0:
|
|
480
|
+
# No auxiliary equation needed for thermal exergy
|
|
481
|
+
# The cost balance will determine c_T,out based on c_T,in and c_heat
|
|
482
|
+
pass
|
|
483
|
+
|
|
484
|
+
elif T_in < T0 and T_out >= T0:
|
|
485
|
+
# Tricky case: inlet below T0, outlet above T0
|
|
486
|
+
logging.warning(
|
|
487
|
+
f"SimpleHeatExchanger '{self.name}': Stream crossing ambient temperature "
|
|
488
|
+
f"during heat absorption not implemented in exergoeconomics yet!"
|
|
489
|
+
)
|
|
449
490
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if self.outl[0]["e_CH"] != 0 else 1)
|
|
456
|
-
equations[counter+2] = f"aux_equality_chem_{self.outl[0]['name']}"
|
|
457
|
-
b[counter+2] = 0
|
|
458
|
-
counter += 3
|
|
459
|
-
else:
|
|
460
|
-
counter += 2
|
|
491
|
+
# Case 2.2: Both streams above ambient temperature
|
|
492
|
+
elif T_in >= T0 and T_out >= T0:
|
|
493
|
+
# No auxiliary equation needed for thermal exergy
|
|
494
|
+
# The cost balance will determine c_T,out based on c_T,in and c_heat
|
|
495
|
+
pass
|
|
461
496
|
|
|
462
|
-
return A, b, counter, equations
|
|
463
|
-
|
|
464
|
-
def exergoeconomic_balance(self, T0):
|
|
465
|
-
r"""
|
|
466
|
-
This function must be implemented in the future.
|
|
497
|
+
return A, b, counter, equations
|
|
467
498
|
|
|
468
|
-
|
|
469
|
-
"""
|
|
470
|
-
|
|
471
|
-
logging.error(
|
|
472
|
-
"The exergoeconomic analysis of SimpleHeatExchanger is not implemented yet. "
|
|
473
|
-
"This method will be implemented in a future release."
|
|
474
|
-
)
|
|
499
|
+
def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
|
|
475
500
|
r"""
|
|
476
501
|
Perform exergoeconomic cost balance for the simple heat exchanger.
|
|
477
502
|
|
|
503
|
+
The general exergoeconomic balance equation is:
|
|
504
|
+
|
|
478
505
|
.. math::
|
|
479
506
|
\dot{C}^{\mathrm{T}}_{\mathrm{in}}
|
|
480
507
|
+ \dot{C}^{\mathrm{M}}_{\mathrm{in}}
|
|
@@ -483,30 +510,25 @@ class SimpleHeatExchanger(Component):
|
|
|
483
510
|
+ \dot{Z}
|
|
484
511
|
= 0
|
|
485
512
|
|
|
486
|
-
In case the chemical exergy of the streams is
|
|
513
|
+
In case the chemical exergy of the streams is known:
|
|
487
514
|
|
|
488
515
|
.. math::
|
|
489
|
-
\dot{C}^{\mathrm{CH}}_{\mathrm{in}
|
|
490
|
-
\dot{C}^{\mathrm{CH}}_{\mathrm{out}
|
|
516
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{in}} =
|
|
517
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{out}}
|
|
491
518
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
\dot{C}^{\mathrm{CH}}_{\mathrm{out},2}
|
|
519
|
+
This method computes cost rates for product and fuel, and derives
|
|
520
|
+
exergoeconomic indicators based on the operating conditions.
|
|
495
521
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
**Heat release** :math:\dot{Q}<0
|
|
522
|
+
**Heat release** (:math:`\dot{Q} < 0`)
|
|
499
523
|
|
|
500
524
|
Case 1: Both streams above ambient temperature
|
|
501
525
|
|
|
502
526
|
.. math::
|
|
503
|
-
|
|
504
527
|
\dot{E}_{\mathrm{P}}
|
|
505
528
|
= \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
506
529
|
- \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
507
530
|
|
|
508
531
|
.. math::
|
|
509
|
-
|
|
510
532
|
\dot{E}_{\mathrm{F}}
|
|
511
533
|
= \dot{E}^{\mathrm{PH}}_{\mathrm{in}}
|
|
512
534
|
- \dot{E}^{\mathrm{PH}}_{\mathrm{out}}
|
|
@@ -514,12 +536,10 @@ class SimpleHeatExchanger(Component):
|
|
|
514
536
|
Case 2: Inlet above and outlet below ambient temperature
|
|
515
537
|
|
|
516
538
|
.. math::
|
|
517
|
-
|
|
518
539
|
\dot{E}_{\mathrm{P}}
|
|
519
540
|
= \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
520
541
|
|
|
521
542
|
.. math::
|
|
522
|
-
|
|
523
543
|
\dot{E}_{\mathrm{F}}
|
|
524
544
|
= \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
525
545
|
+ \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
@@ -529,31 +549,27 @@ class SimpleHeatExchanger(Component):
|
|
|
529
549
|
Case 3: Both streams below ambient temperature
|
|
530
550
|
|
|
531
551
|
.. math::
|
|
532
|
-
|
|
533
552
|
\dot{E}_{\mathrm{P}}
|
|
534
553
|
= \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
535
554
|
- \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
536
555
|
|
|
537
556
|
.. math::
|
|
538
|
-
|
|
539
557
|
\dot{E}_{\mathrm{F}}
|
|
540
558
|
= \bigl(\dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
541
559
|
- \dot{E}^{\mathrm{T}}_{\mathrm{in}}\bigr)
|
|
542
560
|
+ \bigl(\dot{E}^{\mathrm{M}}_{\mathrm{in}}
|
|
543
561
|
- \dot{E}^{\mathrm{M}}_{\mathrm{out}}\bigr)
|
|
544
562
|
|
|
545
|
-
**Heat injection** :math
|
|
563
|
+
**Heat injection** (:math:`\dot{Q} > 0`)
|
|
546
564
|
|
|
547
565
|
Case 1: Both streams above ambient temperature
|
|
548
566
|
|
|
549
567
|
.. math::
|
|
550
|
-
|
|
551
568
|
\dot{E}_{\mathrm{P}}
|
|
552
569
|
= \dot{E}^{\mathrm{PH}}_{\mathrm{out}}
|
|
553
570
|
- \dot{E}^{\mathrm{PH}}_{\mathrm{in}}
|
|
554
571
|
|
|
555
572
|
.. math::
|
|
556
|
-
|
|
557
573
|
\dot{E}_{\mathrm{F}}
|
|
558
574
|
= \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
559
575
|
- \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
@@ -561,13 +577,11 @@ class SimpleHeatExchanger(Component):
|
|
|
561
577
|
Case 2: Inlet below and outlet above ambient temperature
|
|
562
578
|
|
|
563
579
|
.. math::
|
|
564
|
-
|
|
565
580
|
\dot{E}_{\mathrm{P}}
|
|
566
581
|
= \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
567
582
|
+ \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
568
583
|
|
|
569
584
|
.. math::
|
|
570
|
-
|
|
571
585
|
\dot{E}_{\mathrm{F}}
|
|
572
586
|
= \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
573
587
|
+ \bigl(\dot{E}^{\mathrm{M}}_{\mathrm{in}}
|
|
@@ -576,7 +590,6 @@ class SimpleHeatExchanger(Component):
|
|
|
576
590
|
Case 3: Both streams below ambient temperature
|
|
577
591
|
|
|
578
592
|
.. math::
|
|
579
|
-
|
|
580
593
|
\dot{E}_{\mathrm{P}}
|
|
581
594
|
= \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
582
595
|
- \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
@@ -584,25 +597,112 @@ class SimpleHeatExchanger(Component):
|
|
|
584
597
|
- \dot{E}^{\mathrm{M}}_{\mathrm{in}}\bigr)
|
|
585
598
|
|
|
586
599
|
.. math::
|
|
587
|
-
|
|
588
600
|
\dot{E}_{\mathrm{F}}
|
|
589
601
|
= \dot{E}^{\mathrm{T}}_{\mathrm{in}}
|
|
590
602
|
- \dot{E}^{\mathrm{T}}_{\mathrm{out}}
|
|
591
603
|
|
|
592
|
-
Fully dissipative or :math
|
|
604
|
+
**Fully dissipative or** :math:`\dot{Q} = 0`
|
|
593
605
|
|
|
594
606
|
.. math::
|
|
595
|
-
|
|
596
607
|
\dot{E}_{\mathrm{P}} = \mathrm{NaN}
|
|
597
608
|
|
|
598
609
|
.. math::
|
|
599
|
-
|
|
600
610
|
\dot{E}_{\mathrm{F}}
|
|
601
611
|
= \dot{E}^{\mathrm{PH}}_{\mathrm{in}}
|
|
602
612
|
- \dot{E}^{\mathrm{PH}}_{\mathrm{out}}
|
|
603
|
-
|
|
613
|
+
|
|
614
|
+
**Calculated exergoeconomic indicators:**
|
|
615
|
+
|
|
616
|
+
.. math::
|
|
617
|
+
c_{\mathrm{F}} = \frac{\dot{C}_{\mathrm{F}}}{\dot{E}_{\mathrm{F}}}
|
|
618
|
+
|
|
619
|
+
.. math::
|
|
620
|
+
c_{\mathrm{P}} = \frac{\dot{C}_{\mathrm{P}}}{\dot{E}_{\mathrm{P}}}
|
|
621
|
+
|
|
622
|
+
.. math::
|
|
623
|
+
\dot{C}_{\mathrm{D}} = c_{\mathrm{F}} \cdot \dot{E}_{\mathrm{D}}
|
|
624
|
+
|
|
625
|
+
.. math::
|
|
626
|
+
r = \frac{c_{\mathrm{P}} - c_{\mathrm{F}}}{c_{\mathrm{F}}}
|
|
627
|
+
|
|
628
|
+
.. math::
|
|
629
|
+
f = \frac{\dot{Z}}{\dot{Z} + \dot{C}_{\mathrm{D}}}
|
|
630
|
+
|
|
604
631
|
Parameters
|
|
605
632
|
----------
|
|
606
633
|
T0 : float
|
|
607
634
|
Ambient temperature (K).
|
|
635
|
+
chemical_exergy_enabled : bool, optional
|
|
636
|
+
If True, chemical exergy is considered in the calculations.
|
|
637
|
+
Default is False.
|
|
638
|
+
|
|
639
|
+
Attributes Set
|
|
640
|
+
--------------
|
|
641
|
+
C_P : float
|
|
642
|
+
Cost rate of product (currency/time).
|
|
643
|
+
C_F : float
|
|
644
|
+
Cost rate of fuel (currency/time).
|
|
645
|
+
c_P : float
|
|
646
|
+
Specific cost of product (currency/energy).
|
|
647
|
+
c_F : float
|
|
648
|
+
Specific cost of fuel (currency/energy).
|
|
649
|
+
C_D : float
|
|
650
|
+
Cost rate of exergy destruction (currency/time).
|
|
651
|
+
r : float
|
|
652
|
+
Relative cost difference (dimensionless).
|
|
653
|
+
f : float
|
|
654
|
+
Exergoeconomic factor (dimensionless).
|
|
608
655
|
"""
|
|
656
|
+
inlet = self.inl[0]
|
|
657
|
+
outlet = self.outl[0]
|
|
658
|
+
|
|
659
|
+
# Determine heat transfer direction
|
|
660
|
+
Q = outlet["m"] * outlet["h"] - inlet["m"] * inlet["h"]
|
|
661
|
+
|
|
662
|
+
# Case 1: Heat is released (Q < 0)
|
|
663
|
+
if Q < 0:
|
|
664
|
+
if inlet["T"] >= T0 and outlet["T"] >= T0:
|
|
665
|
+
# Both streams above ambient
|
|
666
|
+
self.C_P = outlet["C_T"] - inlet["C_T"]
|
|
667
|
+
self.C_F = inlet["C_PH"] - outlet["C_PH"]
|
|
668
|
+
elif inlet["T"] >= T0 and outlet["T"] < T0:
|
|
669
|
+
# Inlet above, outlet below ambient
|
|
670
|
+
self.C_P = outlet["C_T"]
|
|
671
|
+
self.C_F = inlet["C_T"] + outlet["C_T"] + (inlet["C_M"] - outlet["C_M"])
|
|
672
|
+
elif inlet["T"] <= T0 and outlet["T"] < T0:
|
|
673
|
+
# Both streams below ambient
|
|
674
|
+
self.C_P = outlet["C_T"] - inlet["C_T"]
|
|
675
|
+
self.C_F = self.C_P + (inlet["C_M"] - outlet["C_M"])
|
|
676
|
+
else:
|
|
677
|
+
self.C_P = np.nan
|
|
678
|
+
self.C_F = np.nan
|
|
679
|
+
|
|
680
|
+
# Case 2: Heat is added (Q > 0)
|
|
681
|
+
elif Q > 0:
|
|
682
|
+
if inlet["T"] >= T0 and outlet["T"] >= T0:
|
|
683
|
+
# Both streams above ambient
|
|
684
|
+
self.C_P = outlet["C_PH"] - inlet["C_PH"]
|
|
685
|
+
self.C_F = outlet["C_T"] - inlet["C_T"]
|
|
686
|
+
elif inlet["T"] < T0 and outlet["T"] >= T0:
|
|
687
|
+
# Inlet below, outlet above ambient
|
|
688
|
+
self.C_P = outlet["C_T"] + inlet["C_T"]
|
|
689
|
+
self.C_F = inlet["C_T"] + (inlet["C_M"] - outlet["C_M"])
|
|
690
|
+
elif inlet["T"] < T0 and outlet["T"] <= T0:
|
|
691
|
+
# Both streams below ambient
|
|
692
|
+
self.C_P = inlet["C_T"] - outlet["C_T"] + (outlet["C_M"] - inlet["C_M"])
|
|
693
|
+
self.C_F = inlet["C_T"] - outlet["C_T"]
|
|
694
|
+
else:
|
|
695
|
+
self.C_P = np.nan
|
|
696
|
+
self.C_F = np.nan
|
|
697
|
+
|
|
698
|
+
# Case 3: Fully dissipative or Q == 0
|
|
699
|
+
else:
|
|
700
|
+
self.C_P = np.nan
|
|
701
|
+
self.C_F = inlet["C_PH"] - outlet["C_PH"]
|
|
702
|
+
|
|
703
|
+
# Calculate specific costs and exergoeconomic indicators
|
|
704
|
+
self.c_F = self.C_F / self.E_F if self.E_F else np.nan
|
|
705
|
+
self.c_P = self.C_P / self.E_P if self.E_P else np.nan
|
|
706
|
+
self.C_D = self.c_F * self.E_D if self.E_D else np.nan
|
|
707
|
+
self.r = (self.c_P - self.c_F) / self.c_F if self.c_F else np.nan
|
|
708
|
+
self.f = self.Z_costs / (self.Z_costs + self.C_D) if self.C_D else np.nan
|