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.
- exerpy/__init__.py +2 -4
- exerpy/analyses.py +597 -297
- exerpy/components/__init__.py +3 -0
- exerpy/components/combustion/base.py +157 -114
- exerpy/components/component.py +8 -8
- exerpy/components/heat_exchanger/base.py +593 -256
- exerpy/components/heat_exchanger/condenser.py +353 -166
- exerpy/components/heat_exchanger/simple.py +575 -225
- exerpy/components/heat_exchanger/steam_generator.py +153 -123
- exerpy/components/helpers/cycle_closer.py +61 -34
- exerpy/components/helpers/power_bus.py +117 -0
- exerpy/components/nodes/deaerator.py +221 -102
- exerpy/components/nodes/drum.py +50 -39
- exerpy/components/nodes/flash_tank.py +218 -43
- exerpy/components/nodes/mixer.py +296 -115
- exerpy/components/nodes/splitter.py +173 -0
- exerpy/components/nodes/storage.py +130 -0
- exerpy/components/piping/valve.py +351 -139
- 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 +329 -227
- 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.1.dist-info → exerpy-0.0.3.dist-info}/METADATA +45 -4
- exerpy-0.0.3.dist-info/RECORD +48 -0
- exerpy-0.0.1.dist-info/RECORD +0 -44
- {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/WHEEL +0 -0
- {exerpy-0.0.1.dist-info → exerpy-0.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,24 +2,17 @@ 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 HeatExchanger(Component):
|
|
11
10
|
r"""
|
|
12
|
-
Class for exergy analysis of heat exchangers.
|
|
11
|
+
Class for exergy and exergoeconomic analysis of heat exchangers.
|
|
13
12
|
|
|
14
|
-
This class performs exergy analysis calculations for heat
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Stream 0 represents the hot stream and stream 1 represents the cold stream.
|
|
18
|
-
|
|
19
|
-
Parameters
|
|
20
|
-
----------
|
|
21
|
-
**kwargs : dict
|
|
22
|
-
Arbitrary keyword arguments passed to parent class.
|
|
13
|
+
This class performs exergy and exergoeconomic analysis calculations for heat exchanger components,
|
|
14
|
+
accounting for two inlet and two outlet streams across various temperature regimes, including
|
|
15
|
+
above and below ambient temperature, and optional dissipative behavior.
|
|
23
16
|
|
|
24
17
|
Attributes
|
|
25
18
|
----------
|
|
@@ -32,133 +25,228 @@ class HeatExchanger(Component):
|
|
|
32
25
|
epsilon : float
|
|
33
26
|
Exergetic efficiency of the component :math:`\varepsilon` in :math:`-`.
|
|
34
27
|
inl : dict
|
|
35
|
-
Dictionary containing inlet
|
|
36
|
-
temperature, mass flows, and specific exergies.
|
|
28
|
+
Dictionary containing inlet stream data with mass flows and specific exergies.
|
|
37
29
|
outl : dict
|
|
38
|
-
Dictionary containing outlet
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Case 2 - **All streams below or at ambient temperature**:
|
|
58
|
-
|
|
59
|
-
.. math::
|
|
60
|
-
\dot{E}_\mathrm{P} &= \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{T}_{\mathrm{out,0}}
|
|
61
|
-
- \dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{T}_{\mathrm{in,0}}\\
|
|
62
|
-
\dot{E}_\mathrm{F} &= \dot{m}_{\mathrm{in,1}} \cdot e^\mathrm{PH}_{\mathrm{in,1}}
|
|
63
|
-
- \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{PH}_{\mathrm{out,1}} +
|
|
64
|
-
\dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{M}_{\mathrm{in,0}}
|
|
65
|
-
- \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{M}_{\mathrm{out,0}}
|
|
66
|
-
|
|
67
|
-
Case 3 - **Hot stream inlet/outlet above ambient, cold stream inlet/outlet below ambient**:
|
|
68
|
-
|
|
69
|
-
.. math::
|
|
70
|
-
\dot{E}_\mathrm{P} &= \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{T}_{\mathrm{out,0}}
|
|
71
|
-
+ \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{T}_{\mathrm{out,1}}\\
|
|
72
|
-
\dot{E}_\mathrm{F} &= \dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{PH}_{\mathrm{in,0}}
|
|
73
|
-
+ \dot{m}_{\mathrm{in,1}} \cdot e^\mathrm{PH}_{\mathrm{in,1}}
|
|
74
|
-
- (\dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{M}_{\mathrm{out,0}}
|
|
75
|
-
+ \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{M}_{\mathrm{out,1}})
|
|
76
|
-
|
|
77
|
-
Case 4 - **First inlet above ambient, all other streams below or at ambient**:
|
|
78
|
-
|
|
79
|
-
.. math::
|
|
80
|
-
\dot{E}_\mathrm{P} &= \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{T}_{\mathrm{out,0}}\\
|
|
81
|
-
\dot{E}_\mathrm{F} &= \dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{PH}_{\mathrm{in,0}}
|
|
82
|
-
+ \dot{m}_{\mathrm{in,1}} \cdot e^\mathrm{PH}_{\mathrm{in,1}}
|
|
83
|
-
- (\dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{PH}_{\mathrm{out,1}}
|
|
84
|
-
+ \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{M}_{\mathrm{out,0}})
|
|
85
|
-
|
|
86
|
-
Case 5 - **Hot stream inlet/outlet above ambient, cold stream inlet/outlet below or at ambient**:
|
|
87
|
-
|
|
88
|
-
.. math::
|
|
89
|
-
\dot{E}_\mathrm{P} &= \mathrm{NaN}\\
|
|
90
|
-
\dot{E}_\mathrm{F} &= \dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{PH}_{\mathrm{in,0}}
|
|
91
|
-
- \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{PH}_{\mathrm{out,0}}
|
|
92
|
-
+ \dot{m}_{\mathrm{in,1}} \cdot e^\mathrm{PH}_{\mathrm{in,1}}
|
|
93
|
-
- \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{PH}_{\mathrm{out,1}}
|
|
94
|
-
|
|
95
|
-
Case 6 - **Second outlet above ambient, all others below or at ambient**:
|
|
96
|
-
|
|
97
|
-
.. math::
|
|
98
|
-
\dot{E}_\mathrm{P} &= \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{T}_{\mathrm{out,1}}\\
|
|
99
|
-
\dot{E}_\mathrm{F} &= \dot{m}_{\mathrm{in,0}} \cdot e^\mathrm{PH}_{\mathrm{in,0}}
|
|
100
|
-
- \dot{m}_{\mathrm{out,0}} \cdot e^\mathrm{PH}_{\mathrm{out,0}}
|
|
101
|
-
+ \dot{m}_{\mathrm{in,1}} \cdot e^\mathrm{PH}_{\mathrm{in,1}}
|
|
102
|
-
- \dot{m}_{\mathrm{out,1}} \cdot e^\mathrm{M}_{\mathrm{out,1}}
|
|
103
|
-
|
|
104
|
-
Note that in Case 5, the exergy product :math:`\dot{E}_\mathrm{P}` is undefined (NaN),
|
|
105
|
-
leading to an exergy destruction equal to the exergy fuel:
|
|
106
|
-
:math:`\dot{E}_\mathrm{D} = \dot{E}_\mathrm{F}`.
|
|
107
|
-
|
|
108
|
-
The exergy destruction is calculated as:
|
|
109
|
-
|
|
110
|
-
.. math::
|
|
111
|
-
\dot{E}_\mathrm{D} = \dot{E}_\mathrm{F} - \dot{E}_\mathrm{P}
|
|
112
|
-
|
|
113
|
-
And the exergetic efficiency as:
|
|
114
|
-
|
|
115
|
-
.. math::
|
|
116
|
-
\varepsilon = \frac{\dot{E}_\mathrm{P}}{\dot{E}_\mathrm{F}}
|
|
117
|
-
|
|
118
|
-
Where:
|
|
119
|
-
- :math:`e^\mathrm{T}`: Thermal exergy
|
|
120
|
-
- :math:`e^\mathrm{PH}`: Physical exergy
|
|
121
|
-
- :math:`e^\mathrm{M}`: Mechanical exergy
|
|
30
|
+
Dictionary containing outlet stream data with mass flows and specific exergies.
|
|
31
|
+
Z_costs : float
|
|
32
|
+
Investment cost rate of the component in currency/h.
|
|
33
|
+
C_P : float
|
|
34
|
+
Cost of product stream :math:`\dot{C}_P` in currency/h.
|
|
35
|
+
C_F : float
|
|
36
|
+
Cost of fuel stream :math:`\dot{C}_F` in currency/h.
|
|
37
|
+
C_D : float
|
|
38
|
+
Cost of exergy destruction :math:`\dot{C}_D` in currency/h.
|
|
39
|
+
c_P : float
|
|
40
|
+
Specific cost of product stream (currency per unit exergy).
|
|
41
|
+
c_F : float
|
|
42
|
+
Specific cost of fuel stream (currency per unit exergy).
|
|
43
|
+
r : float
|
|
44
|
+
Relative cost difference, :math:`(c_P - c_F)/c_F`.
|
|
45
|
+
f : float
|
|
46
|
+
Exergoeconomic factor, :math:`\dot{Z}/(\dot{Z} + \dot{C}_D)`.
|
|
47
|
+
Ex_C_col : dict
|
|
48
|
+
Custom cost coefficients collection passed via `kwargs`.
|
|
122
49
|
"""
|
|
123
50
|
|
|
124
51
|
def __init__(self, **kwargs):
|
|
125
|
-
r"""
|
|
52
|
+
r"""
|
|
53
|
+
Initialize the heat exchanger component.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
**kwargs : dict
|
|
58
|
+
Arbitrary keyword arguments. Recognized keys:
|
|
59
|
+
- dissipative (bool): whether component has dissipative behavior, default False
|
|
60
|
+
- Ex_C_col (dict): custom cost coefficients, default {}
|
|
61
|
+
- Z_costs (float): investment cost rate in currency/h, default 0.0
|
|
62
|
+
"""
|
|
126
63
|
self.dissipative = False
|
|
127
64
|
super().__init__(**kwargs)
|
|
128
65
|
|
|
129
66
|
def calc_exergy_balance(self, T0: float, p0: float, split_physical_exergy) -> None:
|
|
130
67
|
r"""
|
|
131
|
-
|
|
68
|
+
Compute the exergy balance of the heat exchanger.
|
|
69
|
+
|
|
70
|
+
Case 1: All streams above ambient temperature
|
|
71
|
+
|
|
72
|
+
If `split_physical_exergy=True`:
|
|
73
|
+
|
|
74
|
+
.. math::
|
|
75
|
+
|
|
76
|
+
\dot{E}_{\mathrm{P}}
|
|
77
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},2}
|
|
78
|
+
- \dot{E}^{\mathrm{T}}_{\mathrm{in},2}
|
|
79
|
+
|
|
80
|
+
.. math::
|
|
81
|
+
|
|
82
|
+
\dot{E}_{\mathrm{F}}
|
|
83
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
84
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
85
|
+
+ \bigl(\dot{E}^{\mathrm{M}}_{\mathrm{in},2}
|
|
86
|
+
- \dot{E}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
87
|
+
|
|
88
|
+
Else:
|
|
89
|
+
|
|
90
|
+
.. math::
|
|
91
|
+
|
|
92
|
+
\dot{E}_{\mathrm{P}}
|
|
93
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
94
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
95
|
+
|
|
96
|
+
.. math::
|
|
97
|
+
|
|
98
|
+
\dot{E}_{\mathrm{F}}
|
|
99
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
100
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
101
|
+
|
|
102
|
+
Case 2: All streams below or equal to ambient temperature
|
|
103
|
+
|
|
104
|
+
If `split_physical_exergy=True`:
|
|
105
|
+
|
|
106
|
+
.. math::
|
|
107
|
+
\dot{E}_{\mathrm{P}}
|
|
108
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},1}
|
|
109
|
+
- \dot{E}^{\mathrm{T}}_{\mathrm{in},1}
|
|
110
|
+
|
|
111
|
+
.. math::
|
|
112
|
+
|
|
113
|
+
\dot{E}_{\mathrm{F}}
|
|
114
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
115
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
116
|
+
+ \bigl(\dot{E}^{\mathrm{M}}_{\mathrm{in},1}
|
|
117
|
+
- \dot{E}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
|
|
118
|
+
|
|
119
|
+
Else
|
|
120
|
+
|
|
121
|
+
.. math::
|
|
122
|
+
\dot{E}_{\mathrm{P}}
|
|
123
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
124
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
125
|
+
|
|
126
|
+
.. math::
|
|
127
|
+
\dot{E}_{\mathrm{F}}
|
|
128
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
129
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
130
|
+
|
|
131
|
+
Case 3: Both stream crossing ambient temperature
|
|
132
|
+
|
|
133
|
+
If `split_physical_exergy=True`:
|
|
134
|
+
|
|
135
|
+
.. math::
|
|
136
|
+
\dot{E}_{\mathrm{P}}
|
|
137
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},1}
|
|
138
|
+
+ \dot{E}^{\mathrm{T}}_{\mathrm{out},2}
|
|
139
|
+
|
|
140
|
+
.. math::
|
|
141
|
+
\dot{E}_{\mathrm{F}}
|
|
142
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
143
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
144
|
+
- \bigl(\dot{E}^{\mathrm{M}}_{\mathrm{out},1}
|
|
145
|
+
+ \dot{E}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
146
|
+
|
|
147
|
+
Else:
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
.. math::
|
|
150
|
+
\dot{E}_{\mathrm{P}}
|
|
151
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},1}
|
|
152
|
+
+ \dot{E}^{\mathrm{T}}_{\mathrm{out},2}
|
|
153
|
+
|
|
154
|
+
.. math::
|
|
155
|
+
\dot{E}_{\mathrm{F}}
|
|
156
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
157
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
158
|
+
|
|
159
|
+
Case 4: Only the hot inlet above ambient temperature
|
|
160
|
+
|
|
161
|
+
If `split_physical_exergy=True`:
|
|
162
|
+
|
|
163
|
+
.. math::
|
|
164
|
+
\dot{E}_{\mathrm{P}}
|
|
165
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},1}
|
|
166
|
+
|
|
167
|
+
.. math::
|
|
168
|
+
\dot{E}_{\mathrm{F}}
|
|
169
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
170
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}\bigr)
|
|
171
|
+
- \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
172
|
+
+ \dot{E}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
|
|
173
|
+
|
|
174
|
+
Else:
|
|
175
|
+
|
|
176
|
+
.. math::
|
|
177
|
+
\dot{E}_{\mathrm{P}}
|
|
178
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
179
|
+
|
|
180
|
+
.. math::
|
|
181
|
+
\dot{E}_{\mathrm{F}}
|
|
182
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
183
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}\bigr)
|
|
184
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
185
|
+
|
|
186
|
+
Case 5: Only the cold inlet below ambient temperature
|
|
187
|
+
|
|
188
|
+
If `split_physical_exergy=True`:
|
|
189
|
+
|
|
190
|
+
.. math::
|
|
191
|
+
\dot{E}_{\mathrm{P}}
|
|
192
|
+
= \dot{E}^{\mathrm{T}}_{\mathrm{out},2}
|
|
193
|
+
|
|
194
|
+
.. math::
|
|
195
|
+
\dot{E}_{\mathrm{F}}
|
|
196
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
197
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
|
|
198
|
+
+ \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
199
|
+
- \dot{E}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
200
|
+
|
|
201
|
+
Else:
|
|
202
|
+
|
|
203
|
+
.. math::
|
|
204
|
+
\dot{E}_{\mathrm{P}}
|
|
205
|
+
= \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
206
|
+
|
|
207
|
+
.. math::
|
|
208
|
+
\dot{E}_{\mathrm{F}}
|
|
209
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
210
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
|
|
211
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
212
|
+
|
|
213
|
+
Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
|
|
214
|
+
|
|
215
|
+
.. math::
|
|
216
|
+
\dot{E}_{\mathrm{P}} = \mathrm{NaN}
|
|
217
|
+
|
|
218
|
+
.. math::
|
|
219
|
+
\dot{E}_{\mathrm{F}}
|
|
220
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
221
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
|
|
222
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
223
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
224
|
+
|
|
225
|
+
If `dissipative` is `True`, the component is treated as dissipative:
|
|
226
|
+
|
|
227
|
+
.. math::
|
|
228
|
+
\dot{E}_{\mathrm{P}} = \mathrm{NaN}
|
|
229
|
+
|
|
230
|
+
.. math::
|
|
231
|
+
\dot{E}_{\mathrm{F}}
|
|
232
|
+
= \bigl(\dot{E}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
233
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
|
|
234
|
+
- \dot{E}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
235
|
+
+ \dot{E}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
136
236
|
|
|
137
237
|
Parameters
|
|
138
238
|
----------
|
|
139
239
|
T0 : float
|
|
140
|
-
Ambient temperature
|
|
240
|
+
Ambient temperature (K).
|
|
141
241
|
p0 : float
|
|
142
|
-
Ambient pressure
|
|
242
|
+
Ambient pressure (Pa).
|
|
143
243
|
split_physical_exergy : bool
|
|
144
|
-
|
|
244
|
+
Whether to split thermal and mechanical exergy.
|
|
145
245
|
|
|
146
246
|
Raises
|
|
147
247
|
------
|
|
148
248
|
ValueError
|
|
149
|
-
If
|
|
150
|
-
|
|
151
|
-
Notes
|
|
152
|
-
-----
|
|
153
|
-
This method updates the following component attributes:
|
|
154
|
-
- E_P (Exergy product)
|
|
155
|
-
- E_F (Exergy fuel)
|
|
156
|
-
- E_D (Exergy destruction)
|
|
157
|
-
- epsilon (Exergetic efficiency)
|
|
158
|
-
|
|
159
|
-
The calculation requires two inlet and two outlet streams, and their
|
|
160
|
-
temperature relationships with ambient temperature determine which case
|
|
161
|
-
of exergy analysis is applied.
|
|
249
|
+
If required inlets or outlets are missing.
|
|
162
250
|
"""
|
|
163
251
|
# Ensure that the component has both inlet and outlet streams
|
|
164
252
|
if len(self.inl) < 2 or len(self.outl) < 2:
|
|
@@ -169,80 +257,125 @@ class HeatExchanger(Component):
|
|
|
169
257
|
|
|
170
258
|
if not self.dissipative:
|
|
171
259
|
# Case 1: All streams are above the ambient temperature
|
|
172
|
-
if all([stream[
|
|
260
|
+
if all([stream["T"] >= T0 for stream in all_streams]):
|
|
173
261
|
if split_physical_exergy:
|
|
174
|
-
self.E_P = self.outl[1][
|
|
175
|
-
self.E_F =
|
|
176
|
-
self.inl[
|
|
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
|
+
)
|
|
177
268
|
else:
|
|
178
|
-
self.E_P = self.outl[1][
|
|
179
|
-
self.E_F = self.inl[0][
|
|
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"]
|
|
180
271
|
|
|
181
272
|
# Case 2: All streams are below or equal to the ambient temperature
|
|
182
|
-
elif all([stream[
|
|
273
|
+
elif all([stream["T"] <= T0 for stream in all_streams]):
|
|
183
274
|
if split_physical_exergy:
|
|
184
|
-
self.E_P = self.outl[0][
|
|
185
|
-
self.E_F =
|
|
186
|
-
self.inl[
|
|
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
|
+
)
|
|
187
281
|
else:
|
|
188
|
-
logging.warning(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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"]
|
|
288
|
+
|
|
289
|
+
# Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= 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
|
+
):
|
|
196
293
|
if split_physical_exergy:
|
|
197
|
-
self.E_P = self.outl[0][
|
|
198
|
-
self.E_F =
|
|
199
|
-
self.
|
|
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
|
+
)
|
|
200
300
|
else:
|
|
201
|
-
logging.warning(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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"]
|
|
307
|
+
|
|
308
|
+
# Case 4: Only hot inlet > 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
|
+
):
|
|
209
312
|
if split_physical_exergy:
|
|
210
|
-
self.E_P = self.outl[0][
|
|
211
|
-
self.E_F =
|
|
212
|
-
self.
|
|
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
|
+
)
|
|
213
319
|
else:
|
|
214
|
-
logging.warning(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
self.inl[1]['m'] * self.inl[1]['e_PH'] - self.outl[1]['m'] * self.outl[1]['e_PH'])
|
|
226
|
-
|
|
227
|
-
# Case 6: Cold inlet is lower ambient, others higher
|
|
228
|
-
else:
|
|
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
|
+
)
|
|
328
|
+
|
|
329
|
+
# Case 5: Only cold inlet <= 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:
|
|
229
331
|
if split_physical_exergy:
|
|
230
|
-
self.E_P = self.outl[1][
|
|
231
|
-
self.E_F =
|
|
232
|
-
self.inl[
|
|
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
|
+
)
|
|
233
338
|
else:
|
|
234
|
-
logging.warning(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
+
|
|
350
|
+
# Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
|
|
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
|
+
):
|
|
354
|
+
self.E_P = np.nan
|
|
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
|
+
|
|
366
|
+
# Case 7: Not implemented case
|
|
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
|
+
)
|
|
239
372
|
|
|
240
373
|
else:
|
|
241
374
|
self.E_F = (
|
|
242
|
-
self.inl[0][
|
|
243
|
-
- self.outl[0][
|
|
244
|
-
- self.outl[1][
|
|
245
|
-
+ self.inl[1][
|
|
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"]
|
|
246
379
|
)
|
|
247
380
|
self.E_P = np.nan
|
|
248
381
|
# Calculate exergy destruction and efficiency
|
|
@@ -254,42 +387,114 @@ class HeatExchanger(Component):
|
|
|
254
387
|
|
|
255
388
|
# Log the results
|
|
256
389
|
logging.info(
|
|
257
|
-
f"HeatExchanger
|
|
390
|
+
f"Exergy balance of HeatExchanger {self.name} calculated: "
|
|
258
391
|
f"E_P={self.E_P:.2f}, E_F={self.E_F:.2f}, E_D={self.E_D:.2f}, "
|
|
259
392
|
f"Efficiency={self.epsilon:.2%}"
|
|
260
393
|
)
|
|
261
394
|
|
|
262
|
-
|
|
263
395
|
def aux_eqs(self, A, b, counter, T0, equations, chemical_exergy_enabled):
|
|
264
|
-
"""
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
396
|
+
r"""
|
|
397
|
+
Add auxiliary cost equations for the heat exchanger.
|
|
398
|
+
|
|
399
|
+
This method appends rows to the cost matrix to enforce:
|
|
400
|
+
|
|
401
|
+
Case 1: All streams above ambient temperature
|
|
402
|
+
|
|
403
|
+
F rule for thermal exergy of the hot stream:
|
|
404
|
+
|
|
405
|
+
.. math::
|
|
406
|
+
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
407
|
+
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
|
|
408
|
+
= 0
|
|
409
|
+
|
|
410
|
+
Case 2: All streams below or equal to ambient temperature
|
|
411
|
+
|
|
412
|
+
F rule for thermal exergy of the cold stream:
|
|
413
|
+
|
|
414
|
+
.. math::
|
|
415
|
+
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
416
|
+
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},2}
|
|
417
|
+
= 0
|
|
418
|
+
|
|
419
|
+
Case 3: Both stream crossing ambient temperature
|
|
420
|
+
|
|
421
|
+
P rule for thermal exergy of both outlets:
|
|
422
|
+
|
|
423
|
+
.. math::
|
|
424
|
+
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
425
|
+
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
426
|
+
= 0
|
|
427
|
+
|
|
428
|
+
Case 4: Only the hot inlet above ambient temperature
|
|
429
|
+
|
|
430
|
+
F rule for thermal exergy of the cold stream:
|
|
431
|
+
|
|
432
|
+
.. math::
|
|
433
|
+
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
434
|
+
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},2}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},2}
|
|
435
|
+
= 0
|
|
436
|
+
|
|
437
|
+
Case 5: Only the cold inlet below ambient temperature
|
|
438
|
+
|
|
439
|
+
F rule for thermal exergy of the hot stream:
|
|
440
|
+
|
|
441
|
+
.. math::
|
|
442
|
+
-\frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{out},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
443
|
+
+ \frac{1}{\dot{E}^{\mathrm{T}}_{\mathrm{in},1}}\,\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
|
|
444
|
+
= 0
|
|
445
|
+
|
|
446
|
+
Case 6: Hot stream always above and cold stream always below ambiente temperature (dissipative case):
|
|
447
|
+
|
|
448
|
+
The dissipative is not handeld here!
|
|
449
|
+
|
|
450
|
+
For all cases, the mechanical and chemical exergy costs are handled as follows:
|
|
451
|
+
|
|
452
|
+
F rule for mechanical exergy of the hot stream:
|
|
453
|
+
|
|
454
|
+
.. math::
|
|
455
|
+
-\frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{out},i}}\,\dot{C}^{\mathrm{M}}_{\mathrm{out},i}
|
|
456
|
+
+ \frac{1}{\dot{E}^{\mathrm{M}}_{\mathrm{in},i}}\,\dot{C}^{\mathrm{M}}_{\mathrm{in},i}
|
|
457
|
+
= 0
|
|
458
|
+
|
|
459
|
+
F rule for chemical exergy on hot branch:
|
|
460
|
+
|
|
461
|
+
.. math::
|
|
462
|
+
-\frac{1}{\dot{E}^{\mathrm{CH}}_{\mathrm{out},i}}\,\dot{C}^{\mathrm{CH}}_{\mathrm{out},i}
|
|
463
|
+
+ \frac{1}{\dot{E}^{\mathrm{CH}}_{\mathrm{in},i}}\,\dot{C}^{\mathrm{CH}}_{\mathrm{in},i}
|
|
464
|
+
= 0
|
|
465
|
+
|
|
268
466
|
Parameters
|
|
269
467
|
----------
|
|
270
468
|
A : numpy.ndarray
|
|
271
|
-
|
|
469
|
+
Current cost matrix.
|
|
272
470
|
b : numpy.ndarray
|
|
273
|
-
|
|
471
|
+
Current RHS vector.
|
|
274
472
|
counter : int
|
|
275
|
-
|
|
473
|
+
Starting row index for auxiliary equations.
|
|
276
474
|
T0 : float
|
|
277
|
-
|
|
278
|
-
equations : dict
|
|
279
|
-
|
|
475
|
+
Ambient temperature (K).
|
|
476
|
+
equations : dict or list
|
|
477
|
+
Structure for equation labels.
|
|
280
478
|
chemical_exergy_enabled : bool
|
|
281
|
-
|
|
479
|
+
Must be True to include chemical exergy mixing.
|
|
480
|
+
|
|
282
481
|
Returns
|
|
283
482
|
-------
|
|
284
483
|
A : numpy.ndarray
|
|
285
|
-
Updated
|
|
484
|
+
Updated cost matrix.
|
|
286
485
|
b : numpy.ndarray
|
|
287
|
-
Updated
|
|
486
|
+
Updated RHS vector.
|
|
288
487
|
counter : int
|
|
289
|
-
Updated row
|
|
290
|
-
equations : dict
|
|
291
|
-
Updated
|
|
488
|
+
Updated row index after adding equations.
|
|
489
|
+
equations : dict or list
|
|
490
|
+
Updated labels.
|
|
491
|
+
|
|
492
|
+
Raises
|
|
493
|
+
------
|
|
494
|
+
ValueError
|
|
495
|
+
If required cost variable indices are missing.
|
|
292
496
|
"""
|
|
497
|
+
|
|
293
498
|
# Equality equation for mechanical and chemical exergy costs.
|
|
294
499
|
def set_equal(A, row, in_item, out_item, var):
|
|
295
500
|
if in_item["e_" + var] != 0 and out_item["e_" + var] != 0:
|
|
@@ -346,48 +551,85 @@ class HeatExchanger(Component):
|
|
|
346
551
|
# Case 1: All temperatures > T0.
|
|
347
552
|
if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
|
|
348
553
|
set_thermal_f_hot(A, counter + 0)
|
|
349
|
-
equations[counter] =
|
|
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
|
+
}
|
|
350
559
|
# Case 2: All temperatures <= T0.
|
|
351
560
|
elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
|
|
352
561
|
set_thermal_f_cold(A, counter + 0)
|
|
353
|
-
equations[counter] =
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
+
}
|
|
567
|
+
# Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= 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:
|
|
357
569
|
set_thermal_p_rule(A, counter + 0)
|
|
358
|
-
equations[counter] =
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
+
}
|
|
575
|
+
# Case 4: Only hot inlet > 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:
|
|
362
577
|
set_thermal_f_cold(A, counter + 0)
|
|
363
|
-
equations[counter] =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
+
}
|
|
583
|
+
# Case 5: Only cold inlet <= 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:
|
|
367
585
|
set_thermal_f_hot(A, counter + 0)
|
|
368
|
-
equations[counter] =
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
+
}
|
|
591
|
+
# Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
|
|
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
|
+
)
|
|
373
597
|
return
|
|
374
|
-
# Case 7:
|
|
598
|
+
# Case 7: Not implemented case
|
|
375
599
|
else:
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
+
|
|
379
605
|
# Mechanical equations (always added)
|
|
380
606
|
set_equal(A, counter + 1, self.inl[0], self.outl[0], "M")
|
|
381
607
|
set_equal(A, counter + 2, self.inl[1], self.outl[1], "M")
|
|
382
|
-
equations[counter + 1] =
|
|
383
|
-
|
|
384
|
-
|
|
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
|
+
|
|
385
619
|
# Only add chemical auxiliary equations if chemical exergy is enabled.
|
|
386
620
|
if chemical_exergy_enabled:
|
|
387
621
|
set_equal(A, counter + 3, self.inl[0], self.outl[0], "CH")
|
|
388
622
|
set_equal(A, counter + 4, self.inl[1], self.outl[1], "CH")
|
|
389
|
-
equations[counter + 3] =
|
|
390
|
-
|
|
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
|
+
}
|
|
391
633
|
num_aux_eqs = 5
|
|
392
634
|
else:
|
|
393
635
|
# Skip chemical auxiliary equations.
|
|
@@ -398,52 +640,147 @@ class HeatExchanger(Component):
|
|
|
398
640
|
|
|
399
641
|
return A, b, counter + num_aux_eqs, equations
|
|
400
642
|
|
|
401
|
-
def exergoeconomic_balance(self, T0):
|
|
402
|
-
"""
|
|
403
|
-
Perform exergoeconomic balance
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
643
|
+
def exergoeconomic_balance(self, T0, chemical_exergy_enabled=False):
|
|
644
|
+
r"""
|
|
645
|
+
Perform exergoeconomic cost balance for the heat exchanger.
|
|
646
|
+
|
|
647
|
+
.. math::
|
|
648
|
+
\dot{C}^{\mathrm{T}}_{\mathrm{in},1}
|
|
649
|
+
+ \dot{C}^{\mathrm{M}}_{\mathrm{in},1}
|
|
650
|
+
+ \dot{C}^{\mathrm{T}}_{\mathrm{in},2}
|
|
651
|
+
+ \dot{C}^{\mathrm{M}}_{\mathrm{in},2}
|
|
652
|
+
- \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
653
|
+
- \dot{C}^{\mathrm{M}}_{\mathrm{out},1}
|
|
654
|
+
- \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
655
|
+
- \dot{C}^{\mathrm{M}}_{\mathrm{out},2}
|
|
656
|
+
+ \dot{Z}
|
|
657
|
+
= 0
|
|
658
|
+
|
|
659
|
+
In case the chemical exergy of the streams is know:
|
|
660
|
+
|
|
661
|
+
.. math::
|
|
662
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{in},1} =
|
|
663
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{out},1}
|
|
664
|
+
|
|
665
|
+
.. math::
|
|
666
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{in},2} =
|
|
667
|
+
\dot{C}^{\mathrm{CH}}_{\mathrm{out},2}
|
|
668
|
+
|
|
669
|
+
This method computes cost coefficients and ratios:
|
|
670
|
+
|
|
671
|
+
Case 1: All streams above ambient temperature
|
|
672
|
+
|
|
673
|
+
.. math::
|
|
674
|
+
\dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
675
|
+
- \dot{C}^{\mathrm{T}}_{\mathrm{in},2}
|
|
676
|
+
|
|
677
|
+
.. math::
|
|
678
|
+
\dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
679
|
+
- \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
680
|
+
+ \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{in},2}
|
|
681
|
+
- \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
682
|
+
|
|
683
|
+
Case 2: All streams below or equal to ambient temperature
|
|
684
|
+
|
|
685
|
+
.. math::
|
|
686
|
+
\dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
687
|
+
- \dot{C}^{\mathrm{T}}_{\mathrm{in},1}
|
|
688
|
+
|
|
689
|
+
.. math::
|
|
690
|
+
\dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
691
|
+
- \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
692
|
+
+ \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{in},1}
|
|
693
|
+
- \dot{C}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
|
|
694
|
+
|
|
695
|
+
Case 3: Both stream crossing ambient temperature
|
|
696
|
+
|
|
697
|
+
.. math::
|
|
698
|
+
\dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
699
|
+
+ \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
700
|
+
|
|
701
|
+
.. math::
|
|
702
|
+
\dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
703
|
+
+ \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
704
|
+
- \bigl(\dot{C}^{\mathrm{M}}_{\mathrm{out},1}
|
|
705
|
+
+ \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
706
|
+
|
|
707
|
+
Case 4: Only the hot inlet above ambient temperature
|
|
708
|
+
|
|
709
|
+
.. math::
|
|
710
|
+
\dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},1}
|
|
711
|
+
|
|
712
|
+
.. math::
|
|
713
|
+
\dot{C}_F = \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
714
|
+
+ \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}\bigr)
|
|
715
|
+
- \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
716
|
+
+ \dot{C}^{\mathrm{M}}_{\mathrm{out},1}\bigr)
|
|
717
|
+
|
|
718
|
+
Case 5: Only the cold inlet below ambient temperature
|
|
719
|
+
|
|
720
|
+
.. math::
|
|
721
|
+
\dot{C}_P = \dot{C}^{\mathrm{T}}_{\mathrm{out},2}
|
|
722
|
+
|
|
723
|
+
.. math::
|
|
724
|
+
\dot{C}_F = \dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
725
|
+
- \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}
|
|
726
|
+
+ \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
727
|
+
- \dot{C}^{\mathrm{M}}_{\mathrm{out},2}\bigr)
|
|
728
|
+
|
|
729
|
+
Case 6: Hot stream always above and cold stream always below ambient temperature (dissipative case):
|
|
730
|
+
|
|
731
|
+
.. math::
|
|
732
|
+
\dot{C}_P = \mathrm{NaN}
|
|
733
|
+
|
|
734
|
+
.. math::
|
|
735
|
+
\dot{C}_F = \bigl(\dot{C}^{\mathrm{PH}}_{\mathrm{in},1}
|
|
736
|
+
- \dot{C}^{\mathrm{PH}}_{\mathrm{out},1}\bigr)
|
|
737
|
+
- \dot{C}^{\mathrm{PH}}_{\mathrm{out},2}
|
|
738
|
+
+ \dot{C}^{\mathrm{PH}}_{\mathrm{in},2}
|
|
739
|
+
|
|
412
740
|
Parameters
|
|
413
741
|
----------
|
|
414
742
|
T0 : float
|
|
415
|
-
Ambient temperature
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
-----
|
|
419
|
-
The exergoeconomic balance considers thermal (T), chemical (CH),
|
|
420
|
-
and mechanical (M) exergy components for the inlet and outlet streams.
|
|
743
|
+
Ambient temperature (K).
|
|
744
|
+
chemical_exergy_enabled : bool, optional
|
|
745
|
+
If True, chemical exergy is considered in the calculations.
|
|
421
746
|
"""
|
|
747
|
+
# Case 1: All streams are above the ambient temperature
|
|
422
748
|
if all([c["T"] > T0 for c in list(self.inl.values()) + list(self.outl.values())]):
|
|
423
749
|
self.C_P = self.outl[1]["C_T"] - self.inl[1]["C_T"]
|
|
424
|
-
self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
|
|
425
|
-
|
|
750
|
+
self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_M"] - self.outl[1]["C_M"])
|
|
751
|
+
# Case 2: All streams are below or equal to the ambient temperature
|
|
426
752
|
elif all([c["T"] <= T0 for c in list(self.inl.values()) + list(self.outl.values())]):
|
|
427
753
|
self.C_P = self.outl[0]["C_T"] - self.inl[0]["C_T"]
|
|
428
|
-
self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (
|
|
429
|
-
|
|
430
|
-
elif
|
|
431
|
-
self.outl[0]["T"] <= T0 and self.inl[1]["T"] <= T0):
|
|
754
|
+
self.C_F = self.inl[1]["C_PH"] - self.outl[1]["C_PH"] + (self.inl[0]["C_M"] - self.outl[0]["C_M"])
|
|
755
|
+
# Case 3: Both stream crossing T0 (hot inlet and cold outlet > T0, hot outlet and cold inlet <= 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:
|
|
432
757
|
self.C_P = self.outl[0]["C_T"] + self.outl[1]["C_T"]
|
|
433
|
-
self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
|
|
434
|
-
|
|
435
|
-
elif
|
|
436
|
-
self.outl[0]["T"] <= T0 and self.outl[1]["T"] <= T0):
|
|
758
|
+
self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[0]["C_M"] + self.outl[1]["C_M"])
|
|
759
|
+
# Case 4: Only hot inlet > 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:
|
|
437
761
|
self.C_P = self.outl[0]["C_T"]
|
|
438
|
-
self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (
|
|
439
|
-
|
|
440
|
-
|
|
762
|
+
self.C_F = self.inl[0]["C_PH"] + self.inl[1]["C_PH"] - (self.outl[1]["C_PH"] + self.outl[0]["C_M"])
|
|
763
|
+
# Case 5: Only cold inlet <= 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:
|
|
441
765
|
self.C_P = self.outl[1]["C_T"]
|
|
442
|
-
self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (
|
|
443
|
-
|
|
766
|
+
self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_PH"] - self.outl[1]["C_M"])
|
|
767
|
+
# Case 6: hot stream always above T0, cold stream always below T0 (dissipative case)
|
|
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
|
+
)
|
|
773
|
+
self.C_P = np.nan
|
|
774
|
+
self.C_F = self.inl[0]["C_PH"] - self.outl[0]["C_PH"] + (self.inl[1]["C_PH"] - self.outl[1]["C_PH"])
|
|
775
|
+
# Case 7: Not implemented case
|
|
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
|
+
)
|
|
444
781
|
|
|
445
782
|
self.c_F = self.C_F / self.E_F
|
|
446
783
|
self.c_P = self.C_P / self.E_P
|
|
447
784
|
self.C_D = self.c_F * self.E_D
|
|
448
785
|
self.r = (self.c_P - self.c_F) / self.c_F
|
|
449
|
-
self.f = self.Z_costs / (self.Z_costs + self.C_D)
|
|
786
|
+
self.f = self.Z_costs / (self.Z_costs + self.C_D)
|