thermofluids 2.0.0__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.
- thermofluids/Base/Quantity.py +16 -0
- thermofluids/Base/Statepoint.py +1191 -0
- thermofluids/Base/__init__.py +0 -0
- thermofluids/Base/thermofluids_registry.txt +5 -0
- thermofluids/Fluids/Drag.py +169 -0
- thermofluids/Fluids/Moody.py +57 -0
- thermofluids/Fluids/PressureDrop.py +322 -0
- thermofluids/Fluids/Re.py +88 -0
- thermofluids/Fluids/__init__.py +4 -0
- thermofluids/HeatTransfer/Gr.py +12 -0
- thermofluids/HeatTransfer/NTU.py +45 -0
- thermofluids/HeatTransfer/Nusselt.py +59 -0
- thermofluids/HeatTransfer/Ra.py +22 -0
- thermofluids/HeatTransfer/TwoPhase.py +249 -0
- thermofluids/HeatTransfer/__init__.py +6 -0
- thermofluids/HeatTransfer/lmtd.py +32 -0
- thermofluids/ProcessEquipment/Column.py +1004 -0
- thermofluids/ProcessEquipment/Compressor.py +167 -0
- thermofluids/ProcessEquipment/Ejector.py +145 -0
- thermofluids/ProcessEquipment/HeatExchanger.py +109 -0
- thermofluids/ProcessEquipment/IsoCompressor.py +124 -0
- thermofluids/ProcessEquipment/Pipe.py +21 -0
- thermofluids/ProcessEquipment/Pump.py +109 -0
- thermofluids/ProcessEquipment/Turbine.py +53 -0
- thermofluids/ProcessEquipment/Valve.py +35 -0
- thermofluids/ProcessEquipment/__init__.py +3 -0
- thermofluids/Utilities/FlowDiagram.py +111 -0
- thermofluids/Utilities/__init__.py +1 -0
- thermofluids/__init__.py +3 -0
- thermofluids-2.0.0.dist-info/METADATA +29 -0
- thermofluids-2.0.0.dist-info/RECORD +32 -0
- thermofluids-2.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
#import matplotlib.pyplot as #plt
|
|
6
|
+
from thermofluids import Statepoint, Quantity
|
|
7
|
+
from CoolProp.CoolProp import PropsSI
|
|
8
|
+
from thermofluids.Properties.BrineProps import get_C_vap_sat, get_C_liq_sat
|
|
9
|
+
from thermofluids.Properties.VLE_data import VLE
|
|
10
|
+
from numpy import linspace, zeros
|
|
11
|
+
|
|
12
|
+
from thermofluids.HeatTransfer.lmtd import lmtd
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Column():
|
|
16
|
+
"""
|
|
17
|
+
This class models distillation processes. There are three performance models:
|
|
18
|
+
|
|
19
|
+
1. ME_balance - This model just does mass and energy balances to determine the inputs and outputs. Use this model when you need high speed calculations and you don't need any info about column design
|
|
20
|
+
2. mccabe - This model does tray-wise mass and (approx.) energy balances using the McCabe Thiele model Use it to get more detailed information about the internal performance of the column with minimal computation time.
|
|
21
|
+
3. mccabe_diabatic - This model does tray-wise mass and (approx.) energy balances using the McCabe Thiele model. The operational line for this model is parallel to the VLE line to model a diabatic process. It solves the model from the top tray downward such that inlet feed and outlet vapor are defined. Use this model as a detailed, functional analysis of a diabatic column.
|
|
22
|
+
4. mccabe_diabatic_bottomup - This model is the same as mccabe_diabatic except that it attempts to solve it from the bottom up such that inlet feed and outlet liquid are defined. DON'T use this model... this function is currently buggy (go with mccabe_diabatic instead)
|
|
23
|
+
5. FullDetail - This model does a complete mass energy balance on every tray. Use it when you need the most internal performance detail, require a column design, and have some time to let it run. This function is currently buggy (go with mccabe_diabatic instead)
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
#currently only setup for 0 reflux columns
|
|
27
|
+
def __init__(self, P_col=None, inlet=None, outlet_vap=None, outlet_liq_c=None, fluid=None):
|
|
28
|
+
self.P_col = P_col
|
|
29
|
+
self.inlet = inlet
|
|
30
|
+
self.outlet_vap = outlet_vap
|
|
31
|
+
self.c_outlet_vap = outlet_vap
|
|
32
|
+
self.outlet_liq_c = outlet_liq_c
|
|
33
|
+
self.fluid = fluid
|
|
34
|
+
|
|
35
|
+
def set_P_col(self, P_col):
|
|
36
|
+
self.P_col = P_col
|
|
37
|
+
|
|
38
|
+
def set_inlet(self, inlet):
|
|
39
|
+
self.inlet = inlet
|
|
40
|
+
|
|
41
|
+
def set_outlet_vap(self, outlet_vap):
|
|
42
|
+
self.outlet_vap = outlet_vap
|
|
43
|
+
|
|
44
|
+
def set_outlet_liq_c(self, outlet_liq_c):
|
|
45
|
+
self.outlet_liq_c = outlet_liq_c
|
|
46
|
+
|
|
47
|
+
def set_fluid(self, fluid):
|
|
48
|
+
self.fluid = fluid
|
|
49
|
+
|
|
50
|
+
def set_Q_sens(self, Q_sens):
|
|
51
|
+
self.Q_sens = Q_sens
|
|
52
|
+
|
|
53
|
+
def set_diabatic(self, dia_targ):
|
|
54
|
+
self.dia_targ = dia_targ
|
|
55
|
+
|
|
56
|
+
def ME_balance(self):
|
|
57
|
+
"""
|
|
58
|
+
This is the simplest column model. It just does mass and energy balances to determine how much heat is
|
|
59
|
+
required to get the desired top and bottom products from the inlet stream.
|
|
60
|
+
|
|
61
|
+
You can use the following results from this model:
|
|
62
|
+
Column.bottoms
|
|
63
|
+
Column.Q_input
|
|
64
|
+
"""
|
|
65
|
+
assert isinstance(self.inlet, Statepoint), '\'inlet\' must be set to a Statepoint'
|
|
66
|
+
assert isinstance(self.outlet_vap, Statepoint), '\'outlet_vap\' must be set to a Statepoint'
|
|
67
|
+
|
|
68
|
+
self.outlet_vap.set_m((self.inlet.m * self.outlet_liq_c - self.inlet.m * self.inlet.c ) / (self.outlet_liq_c - self.outlet_vap.c))
|
|
69
|
+
|
|
70
|
+
self.bottoms = Statepoint(name='bottoms', fluid=self.fluid)
|
|
71
|
+
self.bottoms.set_p(self.P_col)
|
|
72
|
+
self.bottoms.set_x(0.)
|
|
73
|
+
self.bottoms.set_c(self.outlet_liq_c)
|
|
74
|
+
self.bottoms.set_m(self.inlet.m-self.outlet_vap.m)
|
|
75
|
+
|
|
76
|
+
self.Q_input = self.outlet_vap.m*self.outlet_vap.h + self.bottoms.m*self.bottoms.h - self.inlet.m*self.inlet.h
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def mccabe(self, plotfile=None):
|
|
80
|
+
"""
|
|
81
|
+
This is a graphical way to do an approximate mass and energy balance on each tray
|
|
82
|
+
For more info: https://en.wikipedia.org/wiki/McCabe%E2%80%93Thiele_method
|
|
83
|
+
|
|
84
|
+
You can use the following results from this model:
|
|
85
|
+
Column.n_trays
|
|
86
|
+
Produces a png picture file with the McCabe Theile diagram
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# do a mass energy balance to get the bottoms
|
|
90
|
+
self.bottoms = Statepoint(name='bottoms', fluid=self.fluid)
|
|
91
|
+
self.bottoms.set_p(self.P_col)
|
|
92
|
+
self.bottoms.set_x(0.)
|
|
93
|
+
self.bottoms.set_c(self.outlet_liq_c)
|
|
94
|
+
self.bottoms.set_m(self.inlet.m-self.outlet_vap.m)
|
|
95
|
+
|
|
96
|
+
# Convert the flow rates to molar
|
|
97
|
+
fluid2, fluid1 = re.sub(r'^REFPROP-MIX:', '', self.fluid).split('&')
|
|
98
|
+
MW1 = PropsSI('M', 'T', 298., 'P', 1.e5, fluid1)
|
|
99
|
+
MW2 = PropsSI('M', 'T', 298., 'P', 1.e5, fluid2)
|
|
100
|
+
|
|
101
|
+
con_mol_inject = self.inlet.c/MW2 / (self.inlet.c/MW2 + (1.-self.inlet.c)/MW1)
|
|
102
|
+
con_mol_dist = self.outlet_vap.c/MW2 / (self.outlet_vap.c/MW2 + (1.-self.outlet_vap.c)/MW1)
|
|
103
|
+
con_mol_bottoms = self.bottoms.c/MW2 / (self.bottoms.c/MW2 + (1.-self.bottoms.c)/MW1)
|
|
104
|
+
|
|
105
|
+
# Solve for trays
|
|
106
|
+
rect_slope = 0.
|
|
107
|
+
operating_inject_y = con_mol_dist
|
|
108
|
+
strip_slope = (con_mol_dist - con_mol_bottoms) / (con_mol_inject - con_mol_bottoms)
|
|
109
|
+
operating_x = [con_mol_bottoms, con_mol_inject, con_mol_dist] #save operating line data for plotting
|
|
110
|
+
operating_y = [con_mol_bottoms, con_mol_dist, con_mol_dist]
|
|
111
|
+
feed_x = [con_mol_inject]*2 #save injection line data for plotting
|
|
112
|
+
feed_y = [0., con_mol_dist]
|
|
113
|
+
|
|
114
|
+
trays_x = [con_mol_bottoms]*2
|
|
115
|
+
trays_y = [con_mol_bottoms]
|
|
116
|
+
under_target = True
|
|
117
|
+
while under_target:
|
|
118
|
+
trays_y.append(VLE(fluid1,fluid2,trays_x[-1]))
|
|
119
|
+
if trays_y[-1]>con_mol_dist:
|
|
120
|
+
under_target=False #this is enough trays to hit target
|
|
121
|
+
trays_x.append(con_mol_dist)
|
|
122
|
+
trays_y.append(trays_y[-1])
|
|
123
|
+
else:
|
|
124
|
+
if trays_y[-1]<operating_inject_y:
|
|
125
|
+
trays_x.append(trays_x[-1]+(trays_y[-1]-trays_y[-2])/strip_slope)
|
|
126
|
+
else:
|
|
127
|
+
trays_x.append(con_mol_inject+(trays_y[-1]-operating_inject_y)/rect_slope)
|
|
128
|
+
trays_x.append(trays_x[-1])
|
|
129
|
+
trays_y.append(trays_y[-1])
|
|
130
|
+
if len(trays_x)>100:
|
|
131
|
+
raise Exception('Too many trays!')
|
|
132
|
+
|
|
133
|
+
vle_range = [i/100. for i in range(100)]
|
|
134
|
+
vle = [VLE(fluid1, fluid2, con) for con in vle_range]
|
|
135
|
+
|
|
136
|
+
self.n_trays = (len(trays_x)-1)/2
|
|
137
|
+
|
|
138
|
+
#convert to mass
|
|
139
|
+
trays_x_mass = trays_x
|
|
140
|
+
trays_y_mass = trays_y
|
|
141
|
+
for i in range(len(trays_x)):
|
|
142
|
+
trays_x_mass[i] = trays_x[i]*MW2/(trays_x[i]*MW2+(1.-trays_x[i])*MW1)
|
|
143
|
+
trays_y_mass[i] = trays_y[i]*MW2/(trays_y[i]*MW2+(1.-trays_y[i])*MW1)
|
|
144
|
+
vle_range_mass = vle_range
|
|
145
|
+
vle_mass=vle
|
|
146
|
+
for i in range(len(vle)):
|
|
147
|
+
vle_range_mass[i] = vle_range[i]*MW2/(vle_range[i]*MW2+(1.-vle_range[i])*MW1)
|
|
148
|
+
vle_mass[i] = vle[i]*MW2/(vle[i]*MW2+(1.-vle[i])*MW1)
|
|
149
|
+
operating_x_mass=operating_x
|
|
150
|
+
operating_y_mass=operating_y
|
|
151
|
+
for i in range(len(operating_x)):
|
|
152
|
+
operating_x_mass[i] = operating_x_mass[i]*MW2/(operating_x_mass[i]*MW2+(1.-operating_x_mass[i])*MW1)
|
|
153
|
+
operating_y_mass[i] = operating_y_mass[i]*MW2/(operating_y_mass[i]*MW2+(1.-operating_y_mass[i])*MW1)
|
|
154
|
+
feed_x_mass=feed_x
|
|
155
|
+
feed_y_mass=feed_y
|
|
156
|
+
for i in range(len(feed_x)):
|
|
157
|
+
feed_x_mass[i] = feed_x[i]*MW2/(feed_x[i]*MW2+(1.-feed_x[i])*MW1)
|
|
158
|
+
feed_y_mass[i] = feed_y[i]*MW2/(feed_y[i]*MW2+(1.-feed_y[i])*MW1)
|
|
159
|
+
|
|
160
|
+
# plot
|
|
161
|
+
if plotfile != None:
|
|
162
|
+
print('')
|
|
163
|
+
#plt.figure() #make mccabe-thiele diagram
|
|
164
|
+
#plt.subplot(1,1,1)
|
|
165
|
+
#plt.plot(list(range(2)),list(range(2))) #y=x line
|
|
166
|
+
##plt.plot(vle_range,vle) #vapor liquid equilibrium line
|
|
167
|
+
##plt.plot(trays_x,trays_y) #trays line
|
|
168
|
+
##plt.plot(operating_x,operating_y) #flowpath lines
|
|
169
|
+
##plt.plot(feed_x,feed_y)
|
|
170
|
+
#plt.plot(vle_range_mass,vle_mass) #vapor liquid equilibrium line
|
|
171
|
+
#plt.plot(trays_x_mass,trays_y_mass) #trays line
|
|
172
|
+
#plt.plot(operating_x_mass,operating_y_mass) #flowpath lines
|
|
173
|
+
#plt.plot(feed_x_mass,feed_y_mass)
|
|
174
|
+
#plt.xlim(0,1)
|
|
175
|
+
##plt.xlabel('Liquid conc [mol/mol]')
|
|
176
|
+
#plt.xlabel('Liquid conc [kg/kg]')
|
|
177
|
+
#plt.ylim(0,1)
|
|
178
|
+
##plt.ylabel('Vapor conc [mol/mol]')
|
|
179
|
+
#plt.ylabel('Vapor conc [kg/kg]')
|
|
180
|
+
#plt.savefig(plotfile)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_diabatic_curves(self, shift, plotfile=None, printmode=True):
|
|
184
|
+
"""
|
|
185
|
+
This calculates the coordinates for the vapor-liquid equilibrium line and operating line for a given column.
|
|
186
|
+
The operating line is that which minimized exergy destruction. In order to find this operating line, this
|
|
187
|
+
function finds the shift value that will move the VLE line at a 45deg angle southeast such that it satisfies
|
|
188
|
+
the inlet liquid and outlet vapor concentrations. That shift value is used in diabatic_mccabe during the
|
|
189
|
+
McCabe-Thiele calculations.
|
|
190
|
+
(See "Diabatic Column Optimization Compared to Isoforce Columns" by Sauar et al)
|
|
191
|
+
|
|
192
|
+
You can use the following results from this function:
|
|
193
|
+
column.x_sat <-- saturated liquid concentrations for temperature range
|
|
194
|
+
column.y_sat <-- saturated vapor concentrations for temperature range
|
|
195
|
+
column.x_oper <-- operating liquid concentrations for temperature range
|
|
196
|
+
column.y_oper <-- operating vapor concentrations for temperature range
|
|
197
|
+
column.oper_shift <-- value used to make oper. line from VLE line (x_VLE + oper_shift, y_VLE - oper_shift)
|
|
198
|
+
|
|
199
|
+
Written by Fiona Majeau for Rebound Technology July 1 2016
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
# Set up bounds for bisect to find operating line shift
|
|
203
|
+
if shift == None:
|
|
204
|
+
y = self.outlet_vap.c # vapor conc for outlet vap and inlet
|
|
205
|
+
x_min = get_C_liq_sat(self.fluid, y, self.P_col) # sat liq conc for outlet vap (lower bound)
|
|
206
|
+
x_max = self.inlet.c # sat liq conc for inlet (upper bound)
|
|
207
|
+
if x_min > x_max:
|
|
208
|
+
raise Exception('Liquid concentration of outlet vapor (%f) is higher than that of inlet feed (%f). '
|
|
209
|
+
'Decrease outlet vapor conc or increase inlet liquid feed conc.' % (x_min, x_max))
|
|
210
|
+
|
|
211
|
+
# Set up bisect variables
|
|
212
|
+
x_lower = x_min
|
|
213
|
+
x_upper = x_max
|
|
214
|
+
tol = 0.01
|
|
215
|
+
i = 0
|
|
216
|
+
# print 'Bisecting...'
|
|
217
|
+
while True:
|
|
218
|
+
# print 'bisect ', i
|
|
219
|
+
x_guess = (x_lower + x_upper) / 2 # bisect
|
|
220
|
+
shift = x_max - x_guess # find x shift
|
|
221
|
+
y_guess = y + shift # make guess along y = 1-x line from inlet
|
|
222
|
+
y_sat_guess = get_C_vap_sat(self.fluid, x_guess, self.P_col) # find y_sat for given x_guess
|
|
223
|
+
|
|
224
|
+
# Compare y's from y=1-x and VLE calc, check if they are close enough to be done
|
|
225
|
+
if abs(y_guess - y_sat_guess) < tol:
|
|
226
|
+
break # shift is correct
|
|
227
|
+
|
|
228
|
+
# Reassign upper/lower bound depending on which side of root you are on
|
|
229
|
+
if y_guess < y_sat_guess:
|
|
230
|
+
x_upper = x_guess # move upper bound in
|
|
231
|
+
else:
|
|
232
|
+
x_lower = x_guess # move lower bound in
|
|
233
|
+
|
|
234
|
+
# i = i+1
|
|
235
|
+
|
|
236
|
+
if plotfile != None:
|
|
237
|
+
if printmode==True:
|
|
238
|
+
print('\nGETTING VLE AND OPERATING LINES...')
|
|
239
|
+
# Get saturation line for FPS mixture (for plotting)
|
|
240
|
+
x_sat = linspace(0, 1, 101)
|
|
241
|
+
y_sat = zeros(len(x_sat))
|
|
242
|
+
for i in range(len(x_sat)):
|
|
243
|
+
# print 'i =', i
|
|
244
|
+
y_sat[i] = get_C_vap_sat(self.fluid, x_sat[i], self.P_col)
|
|
245
|
+
|
|
246
|
+
# Create min. exergy dest. operational line (for plotting)
|
|
247
|
+
# NOTE: This is the operational line that gives minimum exergy destruction. It is parallel to the saturation
|
|
248
|
+
# line and is calculated by shifting the saturation line diagonally southeast by the outlet liquid composition.
|
|
249
|
+
x_oper = [z + shift for z in x_sat]
|
|
250
|
+
y_oper = [z - shift for z in y_sat]
|
|
251
|
+
|
|
252
|
+
self.x_sat = x_sat
|
|
253
|
+
self.y_sat = y_sat
|
|
254
|
+
self.x_oper = x_oper
|
|
255
|
+
self.y_oper = y_oper
|
|
256
|
+
|
|
257
|
+
# Plot
|
|
258
|
+
#plt.figure() # make McCabe-Thiele diagram
|
|
259
|
+
#plt.subplot(1, 1, 1)
|
|
260
|
+
#plt.plot(list(range(2)), list(range(2))) # y = x line
|
|
261
|
+
#plt.plot(self.x_sat, self.y_sat) # VLE line
|
|
262
|
+
#plt.plot(self.x_oper, self.y_oper) # operational line
|
|
263
|
+
##plt.plot(trays_x, trays_y) # McCabe Thiele points
|
|
264
|
+
#plt.xlim(0, 1)
|
|
265
|
+
#plt.xlabel('Liquid conc [kg/kg]')
|
|
266
|
+
#plt.ylim(0, 1)
|
|
267
|
+
#plt.ylabel('Vapor conc [kg/kg]')
|
|
268
|
+
#plt.savefig(plotfile)
|
|
269
|
+
|
|
270
|
+
self.oper_shift = shift
|
|
271
|
+
# print 'shift =', shift
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def mccabe_diabatic(self, shift=None, plotfile=None, printmode=True):
|
|
275
|
+
"""
|
|
276
|
+
This is a graphical way to do an approximate mass and energy balance on each tray for a diabatic column.
|
|
277
|
+
Creates McCabe-Thiele diagram using an minimum exergy destruction operational line that sets the liquid outlet
|
|
278
|
+
state (bottoms) given Statepoints for the inlet liquid and outlet vapor. Using COE, the necessary Q_sens is
|
|
279
|
+
calculated for each tray.
|
|
280
|
+
(See "Diabatic Column Optimization Compared to Isoforce Columns" by Sauar et al)
|
|
281
|
+
|
|
282
|
+
You can use the following results from this model:
|
|
283
|
+
Column.num_trays <-- number of trays
|
|
284
|
+
Column.Q_feed_to_satliq <-- Q to heat inlet feed to saturated liquid (if subcooled)
|
|
285
|
+
Column.Q_sens <-- array of Q_sens to each tray (1st val is top tray)
|
|
286
|
+
Column.Q_trays_total <-- total Q going to trays (sum of Q_sens)
|
|
287
|
+
Column.Q_total <-- total Q going to column (Q_trays_total + Q_feed_to_satliq)
|
|
288
|
+
Column.tray_temps <-- array of temperature for each tray (1st val is top tray)
|
|
289
|
+
Column.tray_concs <-- array of concentration arrays for each tray (1st val is top tray)
|
|
290
|
+
Column.tray_masses <-- array of mass flow arrays for each tray (1st val is top tray)
|
|
291
|
+
Column.bottoms <-- Statepoint of liquid outlet
|
|
292
|
+
As well as the outputs from get_diabatic_curves (called within)
|
|
293
|
+
column.x_sat <-- saturated liquid concentrations for temperature range
|
|
294
|
+
column.y_sat <-- saturated vapor concentrations for temperature range
|
|
295
|
+
column.x_oper <-- operating liquid concentrations for temperature range
|
|
296
|
+
column.y_oper <-- operating vapor concentrations for temperature range
|
|
297
|
+
column.oper_shift <-- value used to make oper. line from VLE line (x_VLE + oper_shift, y_VLE - oper_shift)
|
|
298
|
+
|
|
299
|
+
Produces a png picture file with the McCabe-Theile diagram (includes VLE line and operating line)
|
|
300
|
+
|
|
301
|
+
Written by Fiona Majeau for Rebound Technology June 30 2016
|
|
302
|
+
Rectifying section added by Luke Erickson for Rebound Technology Nov 30 2016
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
if printmode==True:
|
|
306
|
+
print('\nMcCabe-Thiele method for diabatic column (minimum exergy destruction)')
|
|
307
|
+
print('---------------------------------------------------------------------')
|
|
308
|
+
print('*** TOP DOWN VERSION -- GOOD ***')
|
|
309
|
+
|
|
310
|
+
# Create Statepoint for liquid outlet "bottoms"
|
|
311
|
+
self.bottoms = Statepoint(name='bottoms', fluid=self.fluid, p=self.P_col, x=0.)
|
|
312
|
+
outlet_liq_c_max = self.outlet_liq_c # maximum outlet liquid concentration (used as bound during tray calculations)
|
|
313
|
+
|
|
314
|
+
# Get operating line shift (plus VLE and operating lines for plotting)
|
|
315
|
+
self.get_diabatic_curves(shift=shift, plotfile=plotfile, printmode=printmode)
|
|
316
|
+
# if shift == None:
|
|
317
|
+
|
|
318
|
+
# Create arrays in which to store x and y values for each McCabe point, starting from the top
|
|
319
|
+
trays_x = [self.inlet.c] # top inlet x
|
|
320
|
+
if shift==None:
|
|
321
|
+
shift = self.oper_shift
|
|
322
|
+
trays_y = [self.outlet_vap.c] # top inlet y
|
|
323
|
+
else:
|
|
324
|
+
y_sat_xshift = float(get_C_vap_sat(self.fluid, trays_x[0] - shift, self.P_col))
|
|
325
|
+
trays_y = [y_sat_xshift - shift]
|
|
326
|
+
trays_x.append(float(get_C_liq_sat(self.fluid, trays_y[-1], self.P_col))) # top outlet x
|
|
327
|
+
trays_y.append(trays_y[-1]) # top outlet y
|
|
328
|
+
|
|
329
|
+
# Cleanly store inflows/outflows for each tray for later use in COE
|
|
330
|
+
# NOTE: tray_concs is an array (with num_trays elements) where each element is an array [Vin Lout Vout Lin]
|
|
331
|
+
Vin = 0 # for indexing
|
|
332
|
+
Lout = 1
|
|
333
|
+
Vout = 2
|
|
334
|
+
Lin = 3
|
|
335
|
+
tray_concs = [[0., 0., 0., 0.], [0., 0., 0., 0.]] # store concentrations (tray at 0 index will stay empty)
|
|
336
|
+
num_trays = 1 # start by populating the top tray (tray_conc[1])
|
|
337
|
+
tray_concs[num_trays][Lin] = trays_x[0] # Lin to the top tray
|
|
338
|
+
tray_concs[num_trays][Vout] = trays_y[-1] # Vout of the top tray
|
|
339
|
+
|
|
340
|
+
# Solve for trays, start from the top
|
|
341
|
+
if printmode==True:
|
|
342
|
+
print('\nSOLVING FOR CONCENTRATION IN EACH TRAY... ')
|
|
343
|
+
over_target = True
|
|
344
|
+
while over_target:
|
|
345
|
+
|
|
346
|
+
# Move vertically downward based on operating line shift
|
|
347
|
+
y_sat_xshift = float(get_C_vap_sat(self.fluid, trays_x[-1] - shift, self.P_col))
|
|
348
|
+
|
|
349
|
+
# Check whether movement downward will go into negative y values
|
|
350
|
+
if y_sat_xshift <= shift: # can't have McCabe point there, done with trays
|
|
351
|
+
# Remove last McCabe point (VLE point from horiz. calc)
|
|
352
|
+
trays_x.remove(trays_x[-1])
|
|
353
|
+
trays_y.remove(trays_y[-1])
|
|
354
|
+
num_trays = num_trays - 1
|
|
355
|
+
tray_concs[num_trays][Vin] = 0. # no vapor into bottom tray
|
|
356
|
+
print(tray_concs[num_trays-1])
|
|
357
|
+
print(tray_concs[num_trays])
|
|
358
|
+
print(tray_concs[num_trays+1])
|
|
359
|
+
|
|
360
|
+
#tray_concs[num_trays][Lout] = trays_x[-1] # same as previous point
|
|
361
|
+
break
|
|
362
|
+
|
|
363
|
+
# Add point array
|
|
364
|
+
trays_y.append(y_sat_xshift - shift)
|
|
365
|
+
trays_x.append(trays_x[-1])
|
|
366
|
+
|
|
367
|
+
# Move horizontally with VLE calc
|
|
368
|
+
x_sat_horiz = float(get_C_liq_sat(self.fluid, trays_y[-1], self.P_col))
|
|
369
|
+
trays_x.append(x_sat_horiz)
|
|
370
|
+
trays_y.append(trays_y[-1])
|
|
371
|
+
|
|
372
|
+
# Update tray concentrations
|
|
373
|
+
tray_concs[num_trays][Lout] = trays_x[-2] # second to last value appended
|
|
374
|
+
tray_concs[num_trays][Vin] = trays_y[-1] # last value appended
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# Check whether there is an operating line directly below in anticipation of next vertical calc
|
|
378
|
+
if trays_x[-1] <= shift: # if so, can't have McCabe point there, done with trays
|
|
379
|
+
# Remove last McCabe point (VLE point from horiz. calc)
|
|
380
|
+
trays_x.remove(trays_x[-1])
|
|
381
|
+
trays_y.remove(trays_y[-1])
|
|
382
|
+
tray_concs[num_trays][Vin] = 0. # no vapor into bottom tray
|
|
383
|
+
break
|
|
384
|
+
|
|
385
|
+
# Check whether this tray overshot the target outlet liquid
|
|
386
|
+
elif trays_x[-1] < outlet_liq_c_max: # below target outlet liquid conc, target reached
|
|
387
|
+
# Move vertically downward based on operating line shift to finish off final tray
|
|
388
|
+
num_trays = num_trays + 1
|
|
389
|
+
y_sat_xshift = float(get_C_vap_sat(self.fluid, trays_x[-1] - shift, self.P_col))
|
|
390
|
+
trays_y.append(y_sat_xshift - shift)
|
|
391
|
+
trays_x.append(trays_x[-1])
|
|
392
|
+
tray_concs.append([0., 0., 0., 0.]) # populating last tray
|
|
393
|
+
tray_concs[num_trays][Lin] = tray_concs[num_trays - 1][Lout] # Lin of next tray = Lout of current tray
|
|
394
|
+
tray_concs[num_trays][Vout] = tray_concs[num_trays - 1][Vin] # Vout of next tray = Vin of current tray
|
|
395
|
+
tray_concs[num_trays][Lout] = trays_x[-1] # same as previous point
|
|
396
|
+
tray_concs[num_trays][Vin] = 0. # no vapor into bottom tray
|
|
397
|
+
break
|
|
398
|
+
|
|
399
|
+
# Print current tray
|
|
400
|
+
# print '--------------Tray %.2G --------------' % num_trays
|
|
401
|
+
# print 'Vap_in C = %.3f' % tray_concs[num_trays][Vin], ' Liq_out C = %.3f' % tray_concs[num_trays][Lout]
|
|
402
|
+
# print 'Vap_out C = %.3f' % tray_concs[num_trays][Vout], 'Liq_in C = %.3f' % tray_concs[num_trays][Lin]
|
|
403
|
+
|
|
404
|
+
# More trays to go...
|
|
405
|
+
num_trays = num_trays + 1
|
|
406
|
+
tray_concs.append([0., 0., 0., 0.]) # start populating next tray
|
|
407
|
+
tray_concs[num_trays][Lin] = tray_concs[num_trays - 1][Lout] # Lin of next tray = Lout of current tray
|
|
408
|
+
tray_concs[num_trays][Vout] = tray_concs[num_trays - 1][Vin] # Vout of next tray = Vin of current tray
|
|
409
|
+
|
|
410
|
+
# Check if number of trays has become impractical
|
|
411
|
+
if num_trays > 100:
|
|
412
|
+
raise Exception('Too many trays!')
|
|
413
|
+
|
|
414
|
+
# Solve for rectifier trays from inlet upwards
|
|
415
|
+
if tray_concs[1][Vout] < self.outlet_vap.c - 0.001:
|
|
416
|
+
num_trays = num_trays + 1
|
|
417
|
+
tray_concs[0][Lout] = tray_concs[1][Lin] # Lin of next tray = Lout of current tray
|
|
418
|
+
tray_concs[0][Vin] = tray_concs[1][Vout] # Vout of next tray = Vin of current tray
|
|
419
|
+
while tray_concs[1][Vout] < self.outlet_vap.c - 0.001:
|
|
420
|
+
# Move vertically upward based on operating line shift
|
|
421
|
+
y_sat_xshift = float(get_C_vap_sat(self.fluid, trays_x[0], self.P_col))
|
|
422
|
+
trays_y.insert(0, y_sat_xshift)
|
|
423
|
+
trays_x.insert(0, trays_x[0])
|
|
424
|
+
|
|
425
|
+
# Move horizontally with VLE calc
|
|
426
|
+
x_sat_horiz = float(get_C_liq_sat(self.fluid, trays_y[0] + shift, self.P_col))
|
|
427
|
+
trays_x.insert(0, x_sat_horiz + shift)
|
|
428
|
+
trays_y.insert(0, trays_y[0])
|
|
429
|
+
|
|
430
|
+
# Update tray concentrations
|
|
431
|
+
tray_concs[0][Lin] = trays_x[0] # second value appended
|
|
432
|
+
tray_concs[0][Vout] = trays_y[0] # first value appended
|
|
433
|
+
|
|
434
|
+
# Check whether there is an operating line directly below in anticipation of next vertical calc
|
|
435
|
+
if 1. - trays_x[0] <= shift or trays_y[0] > self.outlet_vap.c - 0.001: # if so, can't have McCabe point there, done with trays
|
|
436
|
+
trays_x.remove(trays_x[0]) # Remove last McCabe point (VLE point from horiz. calc)
|
|
437
|
+
trays_y.remove(trays_y[0])
|
|
438
|
+
tray_concs[0][Lin] = 1. # no liquid into top tray
|
|
439
|
+
tray_concs.insert(0, [0., 0., 0., 0.]) # I'm not sure why but Fiona had a blank tray at the beginning
|
|
440
|
+
break
|
|
441
|
+
|
|
442
|
+
# Check whether this tray overshot the target outlet liquid
|
|
443
|
+
elif trays_y[0] > self.outlet_vap.c - 0.001: # below target outlet liquid conc, target reached
|
|
444
|
+
# Move vertically upward based on operating line shift to finish off final tray
|
|
445
|
+
num_trays = num_trays + 1
|
|
446
|
+
y_sat_xshift = float(get_C_vap_sat(self.fluid, trays_x[0], self.P_col))
|
|
447
|
+
trays_y.insert(0, y_sat_xshift)
|
|
448
|
+
trays_x.insert(0, trays_x[0])
|
|
449
|
+
tray_concs.insert(0, [0., 0., 0., 0.]) # populating last tray
|
|
450
|
+
tray_concs[0][Lin] = 1. # No liquid into top tray
|
|
451
|
+
tray_concs[0][Vout] = trays_y[1] # Outlet vapor from this round of calcs
|
|
452
|
+
tray_concs[0][Lout] = tray_concs[1][Lin] # Outputs liquid demanded by lower tray
|
|
453
|
+
tray_concs[0][Vin] = tray_concs[1][Vout] # Input vapor from lower tray
|
|
454
|
+
break
|
|
455
|
+
|
|
456
|
+
# More trays to go...
|
|
457
|
+
num_trays = num_trays + 1
|
|
458
|
+
tray_concs.insert(0, [0., 0., 0., 0.]) # start populating next tray
|
|
459
|
+
tray_concs[0][Lout] = tray_concs[1][Lin] # Lin of next tray = Lout of current tray
|
|
460
|
+
tray_concs[0][Vin] = tray_concs[1][Vout] # Vout of next tray = Vin of current tray
|
|
461
|
+
|
|
462
|
+
# Check if number of trays has become impractical
|
|
463
|
+
if num_trays > 100:
|
|
464
|
+
raise Exception('Too many trays!')
|
|
465
|
+
|
|
466
|
+
# for tray_conc in tray_concs:
|
|
467
|
+
# print tray_conc
|
|
468
|
+
|
|
469
|
+
# Find Q into each tray, starting from top tray
|
|
470
|
+
if printmode==True:
|
|
471
|
+
print('\nSOLVING FOR Q_SENS INTO EACH TRAY... \n')
|
|
472
|
+
print('(NOTE: Tray 1 is top-most tray)')
|
|
473
|
+
Q_sens = []
|
|
474
|
+
tray_temps = []
|
|
475
|
+
|
|
476
|
+
# Get starting masses
|
|
477
|
+
m_liq_in_i = Quantity(0., 'kg*s^-1')
|
|
478
|
+
self.bottoms.set_c(tray_concs[-1][Lout])
|
|
479
|
+
if not hasattr(self.outlet_vap, 'm'):
|
|
480
|
+
self.outlet_vap.set_m(self.inlet.m * (self.inlet.c - self.bottoms.c) / (self.outlet_vap.c - self.bottoms.c)) # mass balance
|
|
481
|
+
self.outlet_vap.set_p(self.P_col)
|
|
482
|
+
self.outlet_vap.set_x(1.)
|
|
483
|
+
m_vap_out_i = self.outlet_vap.m
|
|
484
|
+
self.bottoms.set_m(self.inlet.m - self.outlet_vap.m)
|
|
485
|
+
feed_tray = [tray_concs.index(t) for t in tray_concs if t[Lin]==self.inlet.c][0]
|
|
486
|
+
|
|
487
|
+
# Check whether inlet feed was subcooled, if so account for Q to bring it up to saturation
|
|
488
|
+
sat_inlet = Statepoint(fluid=self.fluid)
|
|
489
|
+
sat_inlet.set_c(self.inlet.c)
|
|
490
|
+
sat_inlet.set_p(self.P_col)
|
|
491
|
+
sat_inlet.set_x(0.)
|
|
492
|
+
# if sat_inlet.t != self.inlet.t:
|
|
493
|
+
# Account for Q to bring it up to saturation (small delta T at roomish temp, assume same cp)
|
|
494
|
+
Q_feed_to_satliq = self.inlet.m * self.inlet.cp * (sat_inlet.t - self.inlet.t)
|
|
495
|
+
|
|
496
|
+
if printmode==True:
|
|
497
|
+
print('sat_inlet.t =', sat_inlet.t)
|
|
498
|
+
|
|
499
|
+
# Keep track of mass flows in and out of trays
|
|
500
|
+
tray_masses = [[0., 0., 0., 0.], [0., 0., 0., 0.]] # store concentrations (tray at 0 index will stay empty)
|
|
501
|
+
#num_trays = 1 # start by populating the top tray (tray_conc[1])
|
|
502
|
+
|
|
503
|
+
# COE and COM calculations to solve for Q
|
|
504
|
+
self.V = []
|
|
505
|
+
self.L = []
|
|
506
|
+
for i in range(1, num_trays + 1):
|
|
507
|
+
# Find enthalpies using Statepoint calcs
|
|
508
|
+
vap_in_i = Statepoint(fluid=self.fluid, p=self.P_col, x=1., c=float(tray_concs[i][Vin]))
|
|
509
|
+
vap_out_i = Statepoint(fluid=self.fluid, p=self.P_col, x=1., c=float(tray_concs[i][Vout]))
|
|
510
|
+
liq_in_i = Statepoint(fluid=self.fluid, p=self.P_col, x=0., c=float(tray_concs[i][Lin]))
|
|
511
|
+
liq_out_i = Statepoint(fluid=self.fluid, p=self.P_col, x=0., c=float(tray_concs[i][Lout]))
|
|
512
|
+
|
|
513
|
+
tray_temps.append(liq_out_i.t) # liq_out and vap_out will both give tray temp
|
|
514
|
+
|
|
515
|
+
# Mass balance (methanol and total)
|
|
516
|
+
if i == feed_tray:
|
|
517
|
+
m_liq_in_i = m_liq_in_i + self.inlet.m
|
|
518
|
+
##TODO: Figure out what the problem with the mass calc in the last step is
|
|
519
|
+
if i == num_trays:
|
|
520
|
+
m_liq_out_i = self.bottoms.m # small errors become highly magnified at low concentrations
|
|
521
|
+
else:
|
|
522
|
+
m_liq_out_i = (m_liq_in_i * (vap_in_i.c - liq_in_i.c) + m_vap_out_i * (vap_out_i.c - vap_in_i.c)) / \
|
|
523
|
+
(vap_in_i.c - liq_out_i.c)
|
|
524
|
+
m_vap_in_i = m_vap_out_i + m_liq_out_i - m_liq_in_i
|
|
525
|
+
|
|
526
|
+
liq_out_i.set_m(m_liq_out_i)
|
|
527
|
+
liq_in_i.set_m(m_liq_in_i)
|
|
528
|
+
vap_in_i.set_m(m_vap_in_i)
|
|
529
|
+
vap_out_i.set_m(m_vap_out_i)
|
|
530
|
+
self.V.append(vap_out_i)
|
|
531
|
+
self.L.append(liq_in_i)
|
|
532
|
+
|
|
533
|
+
# COE to get heat transfer required
|
|
534
|
+
Q_sens_i = m_vap_out_i * vap_out_i.h + m_liq_out_i * liq_out_i.h - m_vap_in_i * vap_in_i.h - m_liq_in_i * liq_in_i.h
|
|
535
|
+
Q_sens.append(Q_sens_i)
|
|
536
|
+
if printmode==True:
|
|
537
|
+
print('------------------Tray %.2G ------------------' % i)
|
|
538
|
+
print('Q_sens: %.2G kW' % Q_sens_i)
|
|
539
|
+
print('Vap C: %.3f' % vap_out_i.c, 'Vap M: %.4f kg/s' % m_vap_out_i, 'Vap T: %.2f C' % vap_out_i.t)
|
|
540
|
+
print('Liq C: %.3f' % liq_out_i.c, 'Liq M: %.4f kg/s' % m_liq_out_i, 'Liq T: %.2f C' % liq_out_i.t)
|
|
541
|
+
|
|
542
|
+
# Record mass flow rates for tray
|
|
543
|
+
tray_masses.append([0., 0., 0., 0.]) # start populating next tray
|
|
544
|
+
tray_masses[i][Lin] = m_liq_in_i # Lin to the top tray
|
|
545
|
+
tray_masses[i][Vout] = m_vap_out_i # Vout of the top tray
|
|
546
|
+
tray_masses[i][Lout] = m_liq_out_i # Lout to the top tray
|
|
547
|
+
tray_masses[i][Vin] = m_vap_in_i # Vin of the top tray
|
|
548
|
+
|
|
549
|
+
# Move down one tray
|
|
550
|
+
m_vap_out_i = m_vap_in_i
|
|
551
|
+
m_liq_in_i = m_liq_out_i
|
|
552
|
+
|
|
553
|
+
self.L.append(liq_out_i) # add the final two statepoints to the output arrays
|
|
554
|
+
self.V.append(vap_in_i)
|
|
555
|
+
|
|
556
|
+
# for tray in tray_masses:
|
|
557
|
+
# print tray
|
|
558
|
+
|
|
559
|
+
# Plot
|
|
560
|
+
if plotfile != None:
|
|
561
|
+
print('')
|
|
562
|
+
#plt.figure() # make McCabe-Thiele diagram
|
|
563
|
+
#plt.subplot(1, 1, 1)
|
|
564
|
+
#plt.plot(list(range(2)), list(range(2))) # y = x line
|
|
565
|
+
#plt.plot(self.x_sat, self.y_sat) # VLE line
|
|
566
|
+
#plt.plot(self.x_oper, self.y_oper) # operational line
|
|
567
|
+
#plt.plot(trays_x, trays_y) # McCabe Thiele points
|
|
568
|
+
#plt.xlim(0, 1)
|
|
569
|
+
#plt.xlabel('Liquid conc [kg/kg]')
|
|
570
|
+
#plt.ylim(0, 1)
|
|
571
|
+
#plt.ylabel('Vapor conc [kg/kg]')
|
|
572
|
+
#plt.savefig(plotfile)
|
|
573
|
+
|
|
574
|
+
if printmode==True:
|
|
575
|
+
print('\nSUMMARY')
|
|
576
|
+
self.num_trays = num_trays # number of trays discrete trays
|
|
577
|
+
self.feed_tray = feed_tray
|
|
578
|
+
self.Q_feed_to_satliq = Q_feed_to_satliq # Q to bring inlet to saturation
|
|
579
|
+
self.Q_sens = Q_sens # array of Q's to each tray (1st value = top tray)
|
|
580
|
+
self.Q_trays_total = sum(Q_sens) # total Q to trays
|
|
581
|
+
self.Q_trays_heat = sum(Q_sens[(feed_tray-1):]) # total heating requirement
|
|
582
|
+
self.Q_trays_cool = sum(Q_sens[:feed_tray-1]) # total cooling requirement
|
|
583
|
+
self.Q_total = self.Q_trays_total + self.Q_feed_to_satliq # total Q to column
|
|
584
|
+
self.Q_input = self.Q_total
|
|
585
|
+
self.tray_temps = tray_temps # array of temps for each tray (1st value = top tray)
|
|
586
|
+
self.tray_concs = tray_concs # array of concentration arrays for each tray
|
|
587
|
+
self.tray_masses = tray_masses # array of mass flow arrays for each tray
|
|
588
|
+
if printmode==True:
|
|
589
|
+
print('Number of Trays =', num_trays)
|
|
590
|
+
print('Feed at tray ', feed_tray)
|
|
591
|
+
if self.bottoms.c > outlet_liq_c_max:
|
|
592
|
+
print(' NOTE: because of the solving method / operating line constraints, ')
|
|
593
|
+
print(' the outlet liquid vapor concentration is higher than %0.3f' % outlet_liq_c_max)
|
|
594
|
+
print(' Consider adding one more tray to the above number to be safe (%0.0f trays)' % (num_trays + 1))
|
|
595
|
+
print('Vapor outlet conc. =', self.outlet_vap.c)
|
|
596
|
+
print('Liquid outlet conc = %0.3f' % self.bottoms.c)
|
|
597
|
+
print('Q_feed_to_satliq =', self.Q_feed_to_satliq)
|
|
598
|
+
print('Q_trays_heat =', self.Q_trays_heat)
|
|
599
|
+
print('q_trays_cool =', self.Q_trays_cool)
|
|
600
|
+
print('Q_total =', self.Q_total)
|
|
601
|
+
self.trays_x = trays_x
|
|
602
|
+
self.trays_y = trays_y
|
|
603
|
+
|
|
604
|
+
# COM/COE check
|
|
605
|
+
COM = self.outlet_vap.m + self.bottoms.m - self.inlet.m
|
|
606
|
+
COE = (self.outlet_vap.m * self.outlet_vap.h) + (self.bottoms.m * self.bottoms.h) - (self.inlet.m * self.inlet.h) - self.Q_total
|
|
607
|
+
# print 'COM =', COM
|
|
608
|
+
# print 'COE =', COE
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def off_design(self, heating_stream, n_trays, h_tray, d_col, htc, plotfile=None):
|
|
612
|
+
"""
|
|
613
|
+
This model calculates the performance given both an inlet stream and a heating stream.
|
|
614
|
+
Most of the other models are set up to do design, meaning they output column specifications or heat requirements
|
|
615
|
+
This one, in contrast, just outputs performance based on a specified design parameters and inputs.
|
|
616
|
+
|
|
617
|
+
To do:
|
|
618
|
+
|
|
619
|
+
-It currently is set up for only a square column heating from the perimeter. We should make other options available
|
|
620
|
+
-Improve the solver speed. I think the best ways to do this would be to make the conc/mass convergence criteria adaptive and the heat transfer step size adaptive. A bigger but maybe better change would be to do something more complex than damped direct substitution.
|
|
621
|
+
-LMTD for the top tray is currently not accounting for the fact that the Q/T curve is significantly non-linear because that section has both sensible and latent heating. This should be fixed.
|
|
622
|
+
-Set up plotting again.
|
|
623
|
+
-Make outputs more useful.
|
|
624
|
+
|
|
625
|
+
:param heating_stream: A statepoint instance representing the heating fluid coming into the bottom of the column
|
|
626
|
+
:param n_trays: The number of trays [-]
|
|
627
|
+
:param h_tray: The height of a tray [m]
|
|
628
|
+
:param d_col: Width of the column [m]
|
|
629
|
+
:param htc: Heat transfer coefficient [kW/m^2-C]. I typically assume this is equal the HTC of the heating fluid.
|
|
630
|
+
:param plotfile: [string] name of png file to output a mccabe thiele style diagram. Plotting is not fully enabled right now
|
|
631
|
+
:return: Currently just prints all the outputs
|
|
632
|
+
"""
|
|
633
|
+
print("===== Evaluating Problem Boundaries =====")
|
|
634
|
+
sp_all_vap = Statepoint(fluid=self.inlet.fluid, p=self.P_col, c=self.inlet.c, x=1., m=self.inlet.m)
|
|
635
|
+
print('Inlet fluid can absorb: ', self.inlet.m * (sp_all_vap.h - self.inlet.h))
|
|
636
|
+
|
|
637
|
+
sp_max_chilled = Statepoint(fluid=heating_stream.fluid, p=heating_stream.p, t=self.inlet.t, m=heating_stream.m)
|
|
638
|
+
print('Heating stream can supply: ', heating_stream.m * (heating_stream.h - sp_max_chilled.h))
|
|
639
|
+
|
|
640
|
+
print("\n===== Getting VLE =====")
|
|
641
|
+
vle_x = [x/100. for x in range(0, 101, 5)]
|
|
642
|
+
vle_y = [get_C_vap_sat(re.sub(r"\[|\]|[0-9]|\.", "", self.inlet.fluid), x, self.P_col) for x in vle_x]
|
|
643
|
+
|
|
644
|
+
print("\n===== Intializing guesses =====")
|
|
645
|
+
# Initialize heating fluid nodes
|
|
646
|
+
heating_streams = [Statepoint() for n in range(n_trays+1)]
|
|
647
|
+
heating_streams[-1] = heating_stream
|
|
648
|
+
for tray in range(n_trays-1, -1, -1): #step through trays from bottom to top
|
|
649
|
+
heating_streams[tray].set_source(heating_streams[tray+1], ('fluid', 'm', 'p'))
|
|
650
|
+
heating_streams[tray].set_t(self.inlet.t + (heating_stream.t - self.inlet.t)*tray/n_trays) # guess linear temp rise from top to bottom
|
|
651
|
+
|
|
652
|
+
print("\tUsing McCabe Thiele Model to Guess Performance")
|
|
653
|
+
"""
|
|
654
|
+
This section uses the minimum exergy destruction mccabe theile model to get a decent starting point for
|
|
655
|
+
how the column will perform before doing physics-based calculations. The target outlet concentration is varied
|
|
656
|
+
until the number of trays is equal to the number of trays specified for this model.
|
|
657
|
+
"""
|
|
658
|
+
self.num_trays = 0
|
|
659
|
+
vc_guess = get_C_vap_sat(self.fluid, self.inlet.c, self.P_col) - 0.05
|
|
660
|
+
# print vc_guess, self.num_trays
|
|
661
|
+
c_step = 0.02
|
|
662
|
+
last_step = 'none'
|
|
663
|
+
while self.num_trays != n_trays: # Vary the outlet concentration until the number of trays is correct
|
|
664
|
+
self.outlet_vap = Statepoint(fluid=self.fluid, p=self.P_col, x=1., c=vc_guess)
|
|
665
|
+
self.outlet_liq_c = 0.01
|
|
666
|
+
try:
|
|
667
|
+
self.mccabe_diabatic(printmode=False)
|
|
668
|
+
except:
|
|
669
|
+
self.outlet_vap = Statepoint(fluid=self.fluid, p=self.P_col, x=1., c=vc_guess+0.001) # doesnt always converge when # trays is near changing
|
|
670
|
+
if self.num_trays > n_trays:
|
|
671
|
+
if last_step == 'up': # reduce the step size when its crossed past the solution
|
|
672
|
+
c_step = c_step/2.
|
|
673
|
+
vc_guess = vc_guess - c_step
|
|
674
|
+
last_step = 'down'
|
|
675
|
+
elif self.num_trays < n_trays:
|
|
676
|
+
if last_step == 'down':
|
|
677
|
+
c_step = c_step/2.
|
|
678
|
+
vc_guess = vc_guess + c_step
|
|
679
|
+
last_step = 'up'
|
|
680
|
+
print(vc_guess, self.num_trays)
|
|
681
|
+
|
|
682
|
+
Q = list(self.Q_sens)
|
|
683
|
+
V = list(self.V)
|
|
684
|
+
L = list(self.L)
|
|
685
|
+
for i in range(len(V)):
|
|
686
|
+
V[i].name = i
|
|
687
|
+
L[i].name = i
|
|
688
|
+
|
|
689
|
+
qres = 1.
|
|
690
|
+
first_loop = True
|
|
691
|
+
while qres > 0.001: # Outer loop: solve for the heat transfer from the heating stream to the distilled fluid
|
|
692
|
+
clres = 1.
|
|
693
|
+
cvres = 1.
|
|
694
|
+
mlres = 1.
|
|
695
|
+
mvres = 1.
|
|
696
|
+
step = 1.
|
|
697
|
+
print('\nSolving mass balance: ')
|
|
698
|
+
print('Q: ', ['%.2f' % float(q) for q in Q], sum(Q))
|
|
699
|
+
while abs(clres) + abs(cvres) + abs(mlres) + abs(mvres) > 0.001: # Solve for mass flows and concentrations at Qs
|
|
700
|
+
c_bottoms_last = L[-1].c # save values to calculate residual
|
|
701
|
+
c_tops_last = V[0].c
|
|
702
|
+
m_bottoms_last = L[-1].m
|
|
703
|
+
m_tops_last = V[0].m
|
|
704
|
+
|
|
705
|
+
for v in reversed(V[:-2]): # solve COE and check props to get vapor equilibria from bottom to top
|
|
706
|
+
iv = v.name
|
|
707
|
+
new_c = get_C_vap_sat(self.fluid, L[iv+1].c, self.P_col) # property definition
|
|
708
|
+
h_vap_i1 = Statepoint(fluid=self.fluid, x=1, p=self.P_col, c=V[iv+1].c).h - Statepoint(fluid=self.fluid, x=0, p=self.P_col, c=V[iv+1].c).h #TODO: switch this out of sum(m.h)?
|
|
709
|
+
h_vap_i = Statepoint(fluid=self.fluid, x=1, p=self.P_col, c=new_c).h - Statepoint(fluid=self.fluid, x=0, p=self.P_col, c=new_c).h
|
|
710
|
+
new_m = (Q[iv] + V[iv+1].m * h_vap_i1) / h_vap_i # conservation of energy
|
|
711
|
+
V[iv] = Statepoint(name=v.name, fluid=self.fluid, p=v.p, x=v.x, c=new_c, m=new_m)
|
|
712
|
+
|
|
713
|
+
for l in L[1:]: # solve mass balances to get liquid flow and conc from top to bottom
|
|
714
|
+
il = l.name
|
|
715
|
+
new_m = max(L[il-1].m + V[il].m -V[il-1].m, 1e-6) # conservation of total mass
|
|
716
|
+
new_c = (V[il].m * V[il].c + L[il-1].m * L[il-1].c - V[il-1].m * V[il-1].c) / L[il].m # conservation of FPS mass
|
|
717
|
+
new_c = max(new_c, 0.0001)
|
|
718
|
+
new_c = L[il].c * (1.-step) + new_c * step
|
|
719
|
+
L[il] = Statepoint(name=l.name, fluid=self.fluid, p=l.p, x=l.x, c=new_c, m=new_m)
|
|
720
|
+
|
|
721
|
+
if L[-1].m != 0. and mlres != 0.: # I'm not really sure why but this was crashing sometimes without this check
|
|
722
|
+
if (L[-1].m - m_bottoms_last) / L[-1].m / mlres < 0:
|
|
723
|
+
step = step * 0.8
|
|
724
|
+
|
|
725
|
+
clres = (L[-1].c - c_bottoms_last) / L[-1].c
|
|
726
|
+
cvres = (V[0].c - c_tops_last) / V[0].c
|
|
727
|
+
mlres = (L[-1].m - m_bottoms_last) / L[-1].m
|
|
728
|
+
mvres = (V[0].m - m_tops_last) / V[0].m
|
|
729
|
+
sys.stdout.write('\rConc/mass residual: %f' % (abs(clres) + abs(cvres) + abs(mlres) + abs(mvres)))
|
|
730
|
+
print('\nL conc: ', ['%.3f' % l.c for l in L])
|
|
731
|
+
print('V conc: ', ['%.3f' % v.c for v in V])
|
|
732
|
+
|
|
733
|
+
print('Solving heat transfer: ', end=' ')
|
|
734
|
+
Q_last = list(Q)
|
|
735
|
+
for i in reversed(list(range(n_trays))): # resolve heat transfer from bottom up
|
|
736
|
+
q_tray_res = 1.
|
|
737
|
+
while q_tray_res > 0.001:
|
|
738
|
+
heating_streams[i] = Statepoint(source=heating_streams[i], source_vars=('fluid', 'm', 'p'))
|
|
739
|
+
heating_streams[i].set_h(heating_streams[i+1].h - Q[i] / heating_streams[i+1].m)
|
|
740
|
+
|
|
741
|
+
Q_i_last = Q[i]
|
|
742
|
+
try:
|
|
743
|
+
lmtdx = lmtd(heating_streams[i+1].t, heating_streams[i].t, L[i].t, L[i+1].t)
|
|
744
|
+
except AssertionError: # occurs when some steps have a temperature cross.
|
|
745
|
+
lmtdx = 1.e-3
|
|
746
|
+
A = 4. * h_tray * d_col
|
|
747
|
+
Q[i] = htc * A * lmtdx
|
|
748
|
+
|
|
749
|
+
q_tray_res = abs(Q[i] - Q_i_last) / Q[i]
|
|
750
|
+
print('.', end=' ')
|
|
751
|
+
print()
|
|
752
|
+
for i in range(n_trays): # Property calls crashed because the Q changes were causing extreme swings in the concentrations
|
|
753
|
+
q_damp_factor = 0.9
|
|
754
|
+
Q[i] = Q[i] * (1. - q_damp_factor) + Q_last[i] * q_damp_factor # damp the solver so the conc can keep up
|
|
755
|
+
|
|
756
|
+
qres = abs(sum(Q) - sum(Q_last)) / sum(Q)
|
|
757
|
+
if first_loop: # don't let it converge on the first step because its just duplicating the design calcs
|
|
758
|
+
qres = 1
|
|
759
|
+
first_loop = False
|
|
760
|
+
print('Q residual: ', qres)
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
print("\n===== Plotting =====")
|
|
765
|
+
# Plot
|
|
766
|
+
if plotfile != None:
|
|
767
|
+
#plt.figure() # make McCabe-Thiele diagram
|
|
768
|
+
#plt.subplot(1, 1, 1)
|
|
769
|
+
#plt.plot(list(range(2)), list(range(2)), color='gray') # y = x line
|
|
770
|
+
#plt.plot(vle_x, vle_y, color='blue') # VLE line
|
|
771
|
+
print(vle_x)
|
|
772
|
+
print(vle_y)
|
|
773
|
+
print()
|
|
774
|
+
# #plt.plot([tray.c_l_out for tray in trays], [tray.c_v_in for tray in trays]) # operational line
|
|
775
|
+
#plt.plot([l.c for l in L[1:]], [v.c for v in V[1:]], color='green') # operational line
|
|
776
|
+
print([l.c for l in L[1:]])
|
|
777
|
+
print([v.c for v in V[1:]])
|
|
778
|
+
print()
|
|
779
|
+
trays_x = [L[0].c]
|
|
780
|
+
trays_y = [V[0].c, V[0].c]
|
|
781
|
+
for tray in range(n_trays)[1:]:
|
|
782
|
+
trays_x.append(L[tray].c)
|
|
783
|
+
trays_x.append(L[tray].c)
|
|
784
|
+
trays_y.append(V[tray].c)
|
|
785
|
+
trays_y.append(V[tray].c)
|
|
786
|
+
trays_x.append(L[-1].c)
|
|
787
|
+
print(trays_x)
|
|
788
|
+
print(trays_y)
|
|
789
|
+
#plt.plot(trays_x, trays_y, color='red') # McCabe Thiele points
|
|
790
|
+
#plt.xlim(0, 1)
|
|
791
|
+
#plt.xlabel('Liquid conc [kg/kg]')
|
|
792
|
+
#plt.ylim(0, 1)
|
|
793
|
+
#plt.ylabel('Vapor conc [kg/kg]')
|
|
794
|
+
#plt.savefig(plotfile)
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def tray(self,m_vap_in_guess, arg):
|
|
799
|
+
self.tray_outs = [None]*4
|
|
800
|
+
inlet_liq = arg[0]
|
|
801
|
+
outlet_vap = arg[1]
|
|
802
|
+
c_tray = arg[2]
|
|
803
|
+
Fluid = arg[3]
|
|
804
|
+
Q_sens = arg[4]
|
|
805
|
+
assert isinstance(inlet_liq, Statepoint), '\'inlet\' must be set to a Statepoint'
|
|
806
|
+
assert isinstance(outlet_vap, Statepoint), '\'inlet\' must be set to a Statepoint'
|
|
807
|
+
|
|
808
|
+
#this is the vapor coming from the tray below the current tray
|
|
809
|
+
inlet_vap = Statepoint(name='inlet_vap', fluid=Fluid)
|
|
810
|
+
inlet_vap.set_x(1.)
|
|
811
|
+
inlet_vap.set_p(self.P_col)
|
|
812
|
+
|
|
813
|
+
#this is the liquid in the current tray
|
|
814
|
+
outlet_liq = Statepoint(name='outlet_liq', fluid=Fluid)
|
|
815
|
+
outlet_liq.set_c(c_tray)
|
|
816
|
+
outlet_liq.set_x(0.)
|
|
817
|
+
outlet_liq.set_p(self.P_col)
|
|
818
|
+
|
|
819
|
+
count=0
|
|
820
|
+
tol=.002
|
|
821
|
+
stop=0
|
|
822
|
+
while stop <= 1 :
|
|
823
|
+
#update the vapor guess based on the new iteration
|
|
824
|
+
inlet_vap.set_m(m_vap_in_guess)
|
|
825
|
+
outlet_liq.set_m(inlet_vap.m - outlet_vap.m + inlet_liq.m)
|
|
826
|
+
inlet_vap.set_c((outlet_liq.m*outlet_liq.c + outlet_vap.m*outlet_vap.c - inlet_liq.m*inlet_liq.c)/inlet_vap.m)
|
|
827
|
+
|
|
828
|
+
#COE should be 0, so iterate c_guess until it is
|
|
829
|
+
m_vap_in_guess = (outlet_liq.m*outlet_liq.h-inlet_liq.m*inlet_liq.h + outlet_vap.m*outlet_vap.h - Q_sens)/ inlet_vap.h
|
|
830
|
+
m_vap_in_guess_old = m_vap_in_guess
|
|
831
|
+
|
|
832
|
+
COE = abs(float(inlet_liq.m*inlet_liq.h - outlet_liq.m*outlet_liq.h - outlet_vap.m*outlet_vap.h + inlet_vap.m*inlet_vap.h + Q_sens)/(inlet_liq.m*inlet_liq.h+inlet_vap.m*inlet_vap.h))
|
|
833
|
+
COM = abs(float(inlet_liq.m*inlet_liq.c + inlet_vap.m*inlet_vap.c - outlet_liq.m*outlet_liq.c - outlet_vap.m*outlet_vap.c)/(inlet_liq.m*inlet_liq.c + inlet_vap.m*inlet_vap.c)) + abs(float(inlet_liq.m + inlet_vap.m - outlet_liq.m - outlet_vap.m))/(inlet_liq.m + inlet_vap.m)
|
|
834
|
+
res = abs(COE)
|
|
835
|
+
|
|
836
|
+
count=count+1
|
|
837
|
+
|
|
838
|
+
if abs(res)<tol:
|
|
839
|
+
stop=stop+1
|
|
840
|
+
if count > 100:
|
|
841
|
+
warnings.warn('tray did not converge after 100 iteration')
|
|
842
|
+
raise Exception()
|
|
843
|
+
|
|
844
|
+
self.tray_outs = [inlet_vap, outlet_liq, COE, COM]
|
|
845
|
+
|
|
846
|
+
#print
|
|
847
|
+
#print outlet_liq.m, outlet_vap.m, inlet_liq.m, inlet_vap.m
|
|
848
|
+
#print outlet_liq.c, outlet_vap.c, inlet_liq.c, inlet_vap.c
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
def get_trays(self):
|
|
852
|
+
assert isinstance(self.inlet, Statepoint), '\'inlet\' must be set to a Statepoint'
|
|
853
|
+
assert isinstance(self.outlet_vap, Statepoint), '\'outlet_vap\' must be set to a Statepoint'
|
|
854
|
+
|
|
855
|
+
tray_liq = [None]*1
|
|
856
|
+
tray_vap = [None]*1
|
|
857
|
+
self.tray_outs = [None]*4
|
|
858
|
+
|
|
859
|
+
self.bottoms = Statepoint(name='bottoms', fluid=self.fluid)
|
|
860
|
+
self.bottoms.set_p(self.P_col)
|
|
861
|
+
self.bottoms.set_x(0.)
|
|
862
|
+
self.bottoms.set_c(self.outlet_liq_c)
|
|
863
|
+
self.bottoms.set_m(self.inlet.m-self.outlet_vap.m)
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
c_first_tray = self.inlet.c
|
|
867
|
+
|
|
868
|
+
if c_first_tray > self.inlet.c:
|
|
869
|
+
warnings.warn('desired outlet vapor concetration is higher than inlet equilibrium vapor')
|
|
870
|
+
raise Exception()
|
|
871
|
+
|
|
872
|
+
tray_liq[0] = self.inlet
|
|
873
|
+
tray_vap[0] = 0.
|
|
874
|
+
tray_vap.append(self.outlet_vap)
|
|
875
|
+
|
|
876
|
+
Q_input_guess = self.outlet_vap.m*self.outlet_vap.h - self.inlet.m*self.inlet.h + self.bottoms.m*self.bottoms.h
|
|
877
|
+
i=1
|
|
878
|
+
liq_c = tray_liq[0].c
|
|
879
|
+
stop = 0
|
|
880
|
+
while stop==0:
|
|
881
|
+
#for these iterations i = the current tray
|
|
882
|
+
|
|
883
|
+
arg=[None]*1
|
|
884
|
+
arg[0] = tray_liq[i-1] #last tray (i-1) liquid statepoint
|
|
885
|
+
arg.append(tray_vap[i]) #outlet vapor statepoint, this is the inlet vapor calculated in the tray above
|
|
886
|
+
arg.append(get_C_liq_sat(self.fluid,tray_vap[i].c,self.P_col)) #current tray (i) concentration
|
|
887
|
+
arg.append(self.fluid) #column fluid
|
|
888
|
+
arg.append(self.Q_sens) # sensible heat addition
|
|
889
|
+
m_guess = float(tray_vap[i].m)
|
|
890
|
+
self.tray(m_vap_in_guess=m_guess, arg=arg)
|
|
891
|
+
|
|
892
|
+
tray_vap.append(self.tray_outs[0])
|
|
893
|
+
tray_liq.append(self.tray_outs[1]) #this creates the i-1 tray's outlet vapor statepoint, IE, the inlet in the i tray
|
|
894
|
+
print('---------Tray %.2G Converged---------'%i)
|
|
895
|
+
print('COM:%.2G'%self.tray_outs[3], 'COE:%.2G'%self.tray_outs[2])
|
|
896
|
+
print('Vap C: %.3G'%tray_vap[i].c, 'Vap M: %.2G'%tray_vap[i].m, 'Vap T: %.3G'%tray_vap[i].t)
|
|
897
|
+
print('Liq C: %.3G'%tray_liq[i].c, 'Liq M: %.2G'%tray_liq[i].m, 'Liq T: %.3G'%tray_liq[i].t)
|
|
898
|
+
|
|
899
|
+
if tray_liq[i].c<self.outlet_liq_c:
|
|
900
|
+
stop = 1
|
|
901
|
+
self.Q_bottoms = tray_vap[i+1].m*(tray_vap[i+1].h-tray_liq[i].h)
|
|
902
|
+
self.bottoms.set_m(tray_liq[i].m - tray_vap[i+1].m)
|
|
903
|
+
self.bottoms.set_c((tray_liq[i].m*tray_liq[i].c - tray_vap[i+1].m*tray_vap[i+1].c)/self.bottoms.m)
|
|
904
|
+
|
|
905
|
+
i=i+1
|
|
906
|
+
if i > 50:
|
|
907
|
+
warnings.warn('column did not produce outlet condition within 50 trays')
|
|
908
|
+
raise Exception()
|
|
909
|
+
|
|
910
|
+
n_trays = i-1
|
|
911
|
+
self.Q_input = self.Q_bottoms + self.Q_sens*n_trays
|
|
912
|
+
self.Per_dia = (self.Q_sens*n_trays)/self.Q_input
|
|
913
|
+
self.COE_total = abs(tray_liq[0].m*tray_liq[0].h - tray_vap[1].m*tray_vap[1].h - self.bottoms.m*self.bottoms.h + self.Q_input)/self.Q_input
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
# solve flooding equations to get diameter for column
|
|
917
|
+
# pack_f = 693.0 #Cannon packing 0.16" [?m^2/m^3?]
|
|
918
|
+
# HETP = 36.0 #Cannon packing 0.16" [?mm?]
|
|
919
|
+
# pack_f = 92. # Raschig super rings 0.3 (assume same as kempack)
|
|
920
|
+
# HETP = 400. # Raschig super rings 0.3 (http://www.raschig-usa.com/Portals/0/RASCHIG%20SUPER-RING%200_3%20-%20FRI%20Test%20Results.pdf fig 1)
|
|
921
|
+
pack_f = 92. # Kempac 1" Tripack [m^2/m^3]
|
|
922
|
+
HETP = 142. # Kempac 1" Tripack [mm]
|
|
923
|
+
self.d_col = 0.1 #guess diameter
|
|
924
|
+
self.L_col = n_trays*HETP/1000.
|
|
925
|
+
mid_tray = int(round(n_trays/2,1))
|
|
926
|
+
|
|
927
|
+
er=2.
|
|
928
|
+
i=1
|
|
929
|
+
flood_target = 0.3 #flooding target based on Kister p493
|
|
930
|
+
while abs(er)>.01:
|
|
931
|
+
A_cross = self.d_col**2./4.*3.1415
|
|
932
|
+
G_vap = tray_vap[mid_tray].m*3600./A_cross
|
|
933
|
+
G_liq = tray_liq[mid_tray].m*3600./A_cross
|
|
934
|
+
rho_vap = tray_vap[mid_tray].rho
|
|
935
|
+
rho_liq = tray_liq[mid_tray].rho
|
|
936
|
+
X = G_liq/G_vap*(rho_vap/rho_liq)**(0.5)
|
|
937
|
+
Y = ((G_vap*0.205)/3600.)**2.0*pack_f*0.881/(32.2*(rho_vap*0.0624)*((rho_liq*0.0624)-(rho_vap*0.0624)))
|
|
938
|
+
Y_flood = 1.4427*X**2-1.0271*X+0.2312
|
|
939
|
+
per_flood = Y/Y_flood
|
|
940
|
+
er = (flood_target - per_flood)/flood_target
|
|
941
|
+
self.d_col = self.d_col*(1.-er/5.)
|
|
942
|
+
i=i+1
|
|
943
|
+
if i > 50:
|
|
944
|
+
warnings.warn('diameter of column did not converge withing 50 iterations')
|
|
945
|
+
raise Exception()
|
|
946
|
+
self.d_col = Property(float(self.d_col),'m')
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
#print "Gvap", G_vap, "Gliq", G_liq
|
|
950
|
+
|
|
951
|
+
self.n_trays = n_trays
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
if __name__=='__main__':
|
|
961
|
+
#FPS = 'REFPROP-MIX:AMMONIA&WATER'
|
|
962
|
+
#FPS = 'REFPROP-MIX:ACETONE&WATER'
|
|
963
|
+
FPS = 'REFPROP-MIX:METHANOL&WATER'
|
|
964
|
+
|
|
965
|
+
P_col = Property(0.2,'bar')
|
|
966
|
+
liq = Statepoint(name='liq', fluid=FPS)
|
|
967
|
+
vap = Statepoint(name='vap', fluid=FPS)
|
|
968
|
+
liq.set_x(0.)
|
|
969
|
+
liq.set_c(.31)
|
|
970
|
+
liq.set_p(P_col)
|
|
971
|
+
liq.set_m(Property(0.074,'kg*s^-1'))
|
|
972
|
+
|
|
973
|
+
vap.set_x(1.)
|
|
974
|
+
vap.set_c(.84)
|
|
975
|
+
vap.set_p(liq.p)
|
|
976
|
+
# vap.set_m(liq.m*liq.c/vap.c) # mass is overdefined, calculated in the functions
|
|
977
|
+
|
|
978
|
+
col = Column()
|
|
979
|
+
col.set_P_col(P_col)
|
|
980
|
+
col.set_inlet(liq)
|
|
981
|
+
col.set_Q_sens(1.2) #1.2
|
|
982
|
+
col.set_outlet_vap(vap)
|
|
983
|
+
col.set_outlet_liq_c(.01)
|
|
984
|
+
col.set_fluid(FPS)
|
|
985
|
+
|
|
986
|
+
col.ME_balance()
|
|
987
|
+
print(col.Q_input)
|
|
988
|
+
print(col.bottoms)
|
|
989
|
+
# print "Solving adiabatic McCabe Thiele"
|
|
990
|
+
# col.mccabe(plotfile='McCabeThieleDiagram')
|
|
991
|
+
|
|
992
|
+
# print "\n\nSolving COE/COM diabatic model"
|
|
993
|
+
# col.set_diabatic(0.1)
|
|
994
|
+
# step = 0.9
|
|
995
|
+
# count = 10
|
|
996
|
+
# col.get_trays()
|
|
997
|
+
# COM = col.bottoms.m + vap.m - liq.m
|
|
998
|
+
|
|
999
|
+
print('\n\nSolving McCabe Thiele diabatic model')
|
|
1000
|
+
# col.set_outlet_vap(Statepoint(fluid=FPS, p=P_col, x=1., c=0.95))
|
|
1001
|
+
col.mccabe_diabatic(shift=0.03, plotfile='McCabeThieleDiabatic')
|
|
1002
|
+
|
|
1003
|
+
# print "Diameter", col.d_col, "total Q", col.Q_input, "Diabatic", col.Per_dia
|
|
1004
|
+
# print "COM:",COM, "bottoms mass flow:",col.bottoms.m
|