Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download

📚 The CoCalc Library - books, templates and other resources

Views: 96144
License: OTHER
1
2
# coding: utf-8
3
4
# # Equations to derive leaf energy balance components from wind tunnel measurements and compare against leaf model</h1>
5
#
6
#
7
8
# In[1]:
9
10
get_ipython().run_cell_magic(u'capture', u'storage', u"# The above redirects all output of the below commands to the variable 'storage' instead of displaying it.\n# It can be viewed by typing: 'storage()'\n# Setting up worksheet and importing equations from other worksheets\nload('temp/Worksheet_setup.sage')\nload_session('temp/leaf_enbalance_eqs.sobj')\nfun_loadvars(dict_vars) # any variables defined using var2() are saved with their attributes in dict_vars. Here we re-define them based on dict_vars from the other worksheet.")
11
12
13
# In[2]:
14
15
# %load -s fun_SS 'temp/leaf_enbalance_eqs.sage'
16
def fun_SS(vdict1):
17
'''
18
Steady-state T_l, R_ll, H_l and E_l under forced conditions.
19
Parameters are given in a dictionary (vdict) with the following entries:
20
a_s, a_sh, L_l, P_a, P_wa, R_s, Re_c, T_a, g_sw, v_w
21
'''
22
vdict = vdict1.copy()
23
24
# Nusselt number
25
vdict[nu_a] = eq_nua.rhs().subs(vdict)
26
vdict[Re] = eq_Re.rhs().subs(vdict)
27
vdict[Nu] = eq_Nu_forced_all.rhs().subs(vdict)
28
29
# h_c
30
vdict[k_a] = eq_ka.rhs().subs(vdict)
31
vdict[h_c] = eq_hc.rhs().subs(vdict)
32
33
# gbw
34
vdict[D_va] = eq_Dva.rhs().subs(vdict)
35
vdict[alpha_a] = eq_alphaa.rhs().subs(vdict)
36
vdict[rho_a] = eq_rhoa.rhs().subs(vdict)
37
vdict[Le] = eq_Le.rhs().subs(vdict)
38
vdict[g_bw] = eq_gbw_hc.rhs().subs(vdict)
39
40
# Hl, Rll
41
vdict[R_ll] = eq_Rll.rhs().subs(vdict)
42
vdict[H_l] = eq_Hl.rhs().subs(vdict)
43
44
# El
45
vdict[g_tw] = eq_gtw.rhs().subs(vdict)
46
vdict[C_wa] = eq_Cwl.rhs()(P_wl = P_wa, T_l = T_a).subs(vdict)
47
vdict[P_wl] = eq_Pwl.rhs().subs(vdict)
48
vdict[C_wl] = eq_Cwl.rhs().subs(vdict)
49
vdict[E_lmol] = eq_Elmol.rhs().subs(vdict)
50
vdict[E_l] = eq_El.rhs().subs(eq_Elmol).subs(vdict)
51
52
# Tl
53
try:
54
vdict[T_l] = find_root((eq_Rs_enbal - R_s).rhs().subs(vdict), 273, 373)
55
except:
56
print 'too many unknowns for finding T_l: ' + str((eq_Rs_enbal - R_s).rhs().subs(vdict).args())
57
58
# Re-inserting T_l
59
Tlss = vdict[T_l]
60
for name1 in [C_wl, P_wl, R_ll, H_l, E_l, E_lmol]:
61
vdict[name1] = vdict[name1].subs(T_l = Tlss)
62
63
# Test for steady state
64
if n((E_l + H_l + R_ll - R_s).subs(vdict))>1.:
65
return 'error in energy balance: El + Hl + Rll - R_s = ' + str(n((E_l + H_l + R_ll - R_s).subs(vdict)))
66
return vdict
67
68
69
# In[27]:
70
71
# Test
72
73
74
# In[3]:
75
76
# Test
77
vdict = cdict.copy()
78
vdict[a_s] = 1.0 # one sided stomata
79
vdict[g_sw] = 0.01
80
vdict[T_a] = 273 + 25.5
81
vdict[T_w] = vdict[T_a] # Wall temperature equal to air temperature
82
vdict[P_a] = 101325
83
rha = 0.5
84
vdict[P_wa] = rha*eq_Pwl.rhs()(T_l = T_a).subs(vdict)
85
vdict[L_l] = 0.03
86
#vdict[L_A] = vdict[L_l]^2
87
vdict[Re_c] = 3000
88
vdict[R_s] = 0.
89
#vdict[Q_in] = 0
90
vdict[v_w] = 1
91
92
dict_print(fun_SS(vdict))
93
94
95
# ## Gas and energy exchange in a leaf chamber
96
# Calculations based on leaf_capacitance_steady_state1. However, following the LI-6400XT user manual (Eq. 17-3), we replace the air temperature by wall temperature in the calculation of the net longwave balance of the leaf, as wall temperature can be measured in the chamber. Following the same equation, we also add the leaf thermal emissivity of 0.95 (P. 17-3).
97
# **Note that in order to measure sensible heat flux from the leaf, wall temperature must be equal to air temperature!**
98
99
# In[4]:
100
101
width = 0.05
102
height = 0.03
103
volume = 310/(100^3)
104
print 'Volume = ' + str((volume*1000).n()) + ' l'
105
106
print 'min flow rate for flushing = ' + str((volume*3/100^3/60).n()) + ' m3/s'
107
print 'min flow rate for flushing = ' + str((volume*3*1000).n()) + ' l/min'
108
print 'flow rate for 1 m/s direct wind = ' + str(width*height*1) + ' m3/s'
109
print 'flow rate for 1 m/s direct wind = ' + str(width*height*1*1000*60) + ' l/m'
110
print 'flow rate for 5 m/s direct wind = ' + str(width*height*5) + ' m3/s'
111
print 'flow rate for 5 m/s direct wind = ' + str(width*height*5*1000*60) + ' l/m'
112
113
114
# In[5]:
115
116
width = 0.05
117
height = 0.03
118
volume = 400/(100^3)
119
print 'Volume = ' + str((volume*1000).n()) + ' l'
120
121
print 'min flow rate for flushing = ' + str((volume*3/100^3/60).n()) + ' m3/s'
122
print 'min flow rate for flushing = ' + str((volume*3*1000).n()) + ' l/min'
123
print 'flow rate for 1 m/s direct wind = ' + str(width*height*1) + ' m3/s'
124
print 'flow rate for 1 m/s direct wind = ' + str(width*height*1*1000*60) + ' l/m'
125
126
127
# <h2>Chamber insulation material</h2>
128
129
# In[6]:
130
131
var2('c_pi', 'Heat capacity of insulation material', joule/kilogram/kelvin, latexname='c_{pi}')
132
var2('lambda_i', 'Heat conductivity of insulation material', joule/second/meter/kelvin)
133
var2('rho_i', 'Density of insulation material', kilogram/meter^3)
134
var2('L_i', 'Thickness of insulation material', meter)
135
var2('A_i', 'Conducting area of insulation material', meter^2)
136
var2('Q_i', 'Heat conduction through insulation material', joule/second)
137
var2('dT_i', 'Temperature increment of insulation material', kelvin)
138
139
140
# In[7]:
141
142
assumptions(c_pi)
143
144
145
# In[8]:
146
147
eq_Qi = Q_i == dT_i*lambda_i*A_i/L_i
148
units_check(eq_Qi)
149
150
151
# In[9]:
152
153
eq_Li = solve(eq_Qi, L_i)[0]
154
units_check(eq_Li)
155
156
157
# In[10]:
158
159
# The amount of heat absorbed by the insulation material per unit area to increase the wall temperature by the same amount as dT_i for given heat flux Q_i
160
units_check(c_pi*rho_i*dT_i*L_i)
161
162
163
# In[11]:
164
165
(c_pi*rho_i*dT_i*L_i).subs(eq_Li)
166
167
168
# In[12]:
169
170
# From http://www.sager.ch/default.aspx?navid=25, PIR
171
vdict = {}
172
vdict[lambda_i] = 0.022
173
vdict[c_pi] = 1400
174
vdict[rho_i] = 30
175
(c_pi*rho_i*dT_i*L_i).subs(eq_Li).subs(vdict)(A_i = 0.3, dT_i = 0.1, Q_i = 0.01)
176
177
178
# In[13]:
179
180
# From http://www.sager.ch/default.aspx?navid=25, Sagex 15
181
vdict = {}
182
vdict[lambda_i] = 0.038
183
vdict[c_pi] = 1400
184
vdict[rho_i] = 15
185
(c_pi*rho_i*dT_i*L_i).subs(eq_Li).subs(vdict)(A_i = 0.3, dT_i = 0.1, Q_i = 0.01)
186
187
188
# In[14]:
189
190
units_check(lambda_i*A_i*dT_i*L_i)
191
192
193
# In[15]:
194
195
# Assuming a 30x10x5 cm chamber, how thick would the insulation have to be in order to lose
196
# less than 0.01 W heat for 0.1 K dT_i?
197
eq_Li(A_i = 0.3*0.1*2 + 0.3*0.05*2, dT_i = 0.1, Q_i = 0.01).subs(vdict)
198
199
200
# In[16]:
201
202
# From http://www.sager.ch/default.aspx?navid=25, Sagex 30
203
vdict = {}
204
vdict[lambda_i] = 0.033
205
vdict[c_pi] = 1400
206
vdict[rho_i] = 30
207
(c_pi*rho_i*dT_i*L_i).subs(eq_Li).subs(vdict)(A_i = 0.3, dT_i = 0.1, Q_i = 0.01)
208
209
210
# ## Leaf radiation balance
211
212
# In[17]:
213
214
# Additional variables
215
var2('alpha_l', 'Leaf albedo, fraction of shortwave radiation reflected by the leaf', watt/watt)
216
var2('R_d', 'Downwelling global radiation', joule/second/meter^2)
217
var2('R_la', 'Longwave radiation absorbed by leaf', joule/second/meter^2, latexname='R_{la}')
218
var2('R_ld', 'Downwards emitted/reflected global radiation from leaf', joule/second/meter^2, latexname='R_{ld}')
219
var2('R_lu', 'Upwards emitted/reflected global radiation from leaf', joule/second/meter^2, latexname='R_{lu}')
220
var2('R_u', 'Upwelling global radiation', joule/second/meter^2)
221
var2('S_a', 'Radiation sensor above leaf reading', joule/second/meter^2)
222
var2('S_b', 'Radiation sensor below leaf reading', joule/second/meter^2)
223
var2('S_s', 'Radiation sensor beside leaf reading', joule/second/meter^2)
224
225
226
# ### Measuring radiative exchange
227
#
228
# <p>The leaf is exposed to downwelling radiation ($R_d$) originating from shortwave irradiance entering through the glass window plus the longwave irradiance transmitted througha and emitted by the glass window, plus the upwelling radiation ($R_u$) emitted by the bottom glass window.</p>
229
# <p>The leaf itself reflects some of the radiation in both direction and emits its own black body longwave radiation. The sum of refelcted and emitted radiation away from the leaf is denoted as $R_{lu}$ and $R_{ld}$ for upward and downwards respectively. We have three net radiation sensors in place, one above the leaf measuring $S_a$, one below the leaf measureing $S_b$ and one at the same level beside the leaf measureing $S_s$. These sensor measure:</p>
230
# <p><img style="float: right;" src="figures/Leaf_radbalance.png" alt="" width="400" height="300" /></p>
231
# <p>$S_a = R_d - R_{lu}$</p>
232
# <p>$S_b = R_{ld} - R_u$</p>
233
# <p>$S_s = R_d - R_u$</p>
234
# <p>This leaves us with 3 equations with 4 unknows, so we either have to assume that $R_{lu} = R_{ld}$, assuming that both sides of the leaf have the same temperature or $R_u = 0$ to solve the algebraic problem. In daylight, $R_d >> R_u$, so this assumption should not lead to a big bias, however this would imply that $S_b = R_{ld}$, which is certainly incorrect.</p>
235
# <p>Unfortunately, the assumption $R_{lu} = R_{ld}$ does not help solve the problem as it just implies that $S_s = S_a + S_b$:</p>
236
237
# In[18]:
238
239
eq_Rs_Rd = R_s == (1-alpha_l)*R_d
240
eq_Sa = S_a == R_d - R_lu
241
eq_Sb = S_b == R_ld - R_u
242
eq_Ss = S_s == R_d - R_u
243
244
245
# In[19]:
246
247
# Assuming R_lu = R_ld
248
eq_assRldRlu = R_ld == R_lu
249
solve([eq_Sa, eq_Sb.subs(eq_assRldRlu), eq_Ss], R_d, R_lu, R_u)
250
251
252
# In[20]:
253
254
# More specifically,
255
eq1 = solve(eq_Sa, R_d)[0]
256
eq2 = solve(eq_Sb, R_ld)[0].subs(eq_assRldRlu)
257
eq3 = solve(eq_Ss, R_u)[0]
258
solve(eq1.subs(eq2).subs(eq3), S_s)
259
260
261
# In[21]:
262
263
# Assuming that R_u = 0
264
eq_assRu0 = R_u == 0
265
soln = solve([eq_Sa, eq_Sb, eq_Ss, eq_assRu0], R_d, R_lu, R_ld, R_u)
266
print soln
267
268
269
# In[22]:
270
271
#eq_Rd = soln[0][0]
272
#eq_Rlu = soln[0][1]
273
#eq_Rld = soln[0][2]
274
#eq_Rlu
275
276
277
# <p>However, what we can do in any case is to quantify the net radiative energy absorbed by the leaf as</p>
278
# <p>$\alpha_l R_s - R_{ll} = S_a - S_b$:</p>
279
280
# In[23]:
281
282
# Leaf radiation balance
283
eq_Rs_Rll = R_s - R_ll == R_d + R_u - R_lu - R_ld
284
eq_Rbalance = R_s - R_ll == S_a - S_b
285
286
287
# In[24]:
288
289
solve([eq_Sa, eq_Sb, eq_Ss, R_d + R_u - R_lu - R_ld == S_a - S_b], R_d, R_lu, R_ld, R_u)
290
291
292
# ## Leaf water vapour exchange and energy balace
293
# The leaf water vapour exchange and energy balance equations were imported from [leaf_enbalance_eqs](Leaf_enbalance_eqs.ipynb). Here we will use an additional equation to estimate the thickness of the leaf boundary layer and get a feeling for the minimum distance between leaf and sensors to avoid interference with the boundary layer conditions.
294
295
# In[25]:
296
297
# Blasius solution for BL thickness (http://en.wikipedia.org/wiki/Boundary-layer_thickness)
298
var2('B_l', 'Boundary layer thickness', meter)
299
vdict = cdict.copy()
300
Ta = 300
301
vdict[T_a] = Ta
302
vdict[T_l] = Ta
303
vdict[L_l] = 0.15
304
vdict[v_w] = 0.5
305
vdict[Re_c] = 3000
306
vdict[a_s] = 1
307
vdict[P_a] = 101325
308
vdict[P_wa] = 0
309
eq_Bl = B_l == 4.91*sqrt(nu_a*L_l/v_w)
310
print eq_Bl.subs(eq_nua).subs(vdict)
311
eq_gbw_hc.subs(eq_hc).subs(eq_Nu_forced_all).subs(eq_Re).subs(eq_rhoa).subs(eq_Le).subs(eq_Dva).subs(eq_alphaa, eq_nua, eq_ka).subs(vdict)
312
313
314
# In[26]:
315
316
vdict[L_l] = 0.03
317
vdict[v_w] = 8
318
print eq_Bl.subs(eq_nua).subs(vdict)
319
eq_gbw_hc.subs(eq_hc).subs(eq_Nu_forced_all).subs(eq_Re).subs(eq_rhoa).subs(eq_Le).subs(eq_Dva).subs(eq_alphaa, eq_nua, eq_ka).subs(vdict)
320
321
322
# In[27]:
323
324
# How does B_l scale with wind speed?
325
vdict[v_w] = v_w
326
P = plot(eq_Bl.rhs().subs(eq_nua).subs(vdict), (v_w, 0.5,5))
327
P.axes_labels(['Wind speed (m s$^{-1}$)', 'Boundary layer thickness (m)'])
328
P
329
330
331
# In[28]:
332
333
# Maximum sensible heat flux of a 3x3 cm leaf irradiated by 600 W/m2
334
600*0.03^2
335
336
337
# <h1>Chamber mass and energy balance</h1>
338
# <p>Usually, we know the volumetric inflow into the chamber, so to convert to molar inflow (mol s$^{-1}$), we will use the ideal gas law: $P_a V_c = n R_{mol} T_{in}$, where $n$ is the amount of matter in the chamber (mol). To convert from a volume to a flow rate, we replace $V_c$ by $F_{in,v}$. Note that partial pressures of dry air and vapour are additive, such that</p>
339
# <p>$P_a = P_w + P_d$</p>
340
# <p>However, the volumes are not additive, meaning that:</p>
341
# <p>$P_d V_a = n_d R_{mol} T_{a}$</p>
342
# <p>$(P_a - P_d) V_a = n_a R_{mol} T_{a}$</p>
343
# <p>i.e. we use the same volume ($V_a$) for both the vapour and the dry air. This is because both the vapour and the dry air are well mixed and occupy the same total volume. Their different amounts are expressed in their partial pressures. If we wanted to calculate the partial volumes they would take up in isolation from each other, we would need to specify at which pressure this volume is taken up and if we used the same pressure for both (e.g. $P_a$), we would obtain a volume fraction for water vapour equivalent to its partial pressure fraction in the former case.</p>
344
# <p>Therefore, we will distinguish the molar flow rates of water vapour ($F_{in,mol,v}$) and dry air ($F_{in,mol,a}$) but they share a common volumetric flow rate ($F_{in,v}$).</p>
345
346
# In[29]:
347
348
var2('W_c', 'Chamber width', meter)
349
var2('L_c', 'Chamber length', meter)
350
var2('H_c', 'Chamber height', meter)
351
var2('V_c', 'Chamber volume', meter^3)
352
var2('n_c', 'molar mass of gas in chamber', mole)
353
var2('F_in_v', 'Volumetric flow rate into chamber', meter^3/second, latexname='F_{in,v}')
354
var2('F_in_mola', 'Molar flow rate of dry air into chamber', mole/second, latexname='F_{in,mol,a}')
355
var2('F_in_molw', 'Molar flow rate of water vapour into chamber', mole/second, latexname='F_{in,mol,w}')
356
var2('F_out_mola', 'Molar flow rate of dry air out of chamber', mole/second, latexname='F_{out,mol,a}')
357
var2('F_out_molw', 'Molar flow rate of water vapour out of chamber', mole/second, latexname='F_{out,mol,w}')
358
var2('F_out_v', 'Volumetric flow rate out of chamber', meter^3/second, latexname='F_{out,v}')
359
var2('T_d', 'Dew point temperature of incoming air', kelvin)
360
var2('T_in', 'Temperature of incoming air', kelvin, latexname='T_{in}')
361
var2('T_out', 'Temperature of outgoing air (= chamber T_a)', kelvin, latexname='T_{out}')
362
var2('T_room', 'Lab air temperature', kelvin, latexname='T_{room}')
363
var2('P_w_in', 'Vapour pressure of incoming air', pascal, latexname='P_{w,in}')
364
var2('P_w_out', 'Vapour pressure of outgoing air', pascal, latexname='P_{w,out}')
365
var2('R_H_in', 'Relative humidity of incoming air', latexname='R_{H,in}')
366
var2('L_A', 'Leaf area', meter^2)
367
368
369
# In[30]:
370
371
eq_V_c = fun_eq(V_c == W_c*L_c*H_c)
372
eq_F_in_mola = fun_eq(F_in_mola == (P_a - P_w_in)*F_in_v/(R_mol*T_in))
373
eq_F_in_molw = fun_eq(F_in_molw == (P_w_in)*F_in_v/(R_mol*T_in))
374
eq_F_out_mola = fun_eq(F_out_mola == (P_a - P_w_out)*F_out_v/(R_mol*T_out))
375
eq_F_out_molw = fun_eq(F_out_molw == (P_w_out)*F_out_v/(R_mol*T_out))
376
eq_F_out_v = fun_eq(F_out_v == (F_out_mola + F_out_molw)*R_mol*T_out/P_a)
377
378
379
# <p>At steady state, $F_{out,mola} = F_{in,mola}$ and $F_{out,molw} = F_{in,molw} + E_{l,mol} L_A$. In the presence of evaporation, we can simply add Elmol to get F_out_v as a function of F_in_v</p>
380
# <p>Assuming that the pressure inside the chamber is constant and equal to the pressure outside, we compute the change in volumetric outflow due to a change in temperature and due to the input of water vapour by transpiration as:</p>
381
382
# In[31]:
383
384
eq_Foutv_Finv_Tout = eq_F_out_v.subs(F_out_mola = F_in_mola, F_out_molw = F_in_molw + E_lmol*L_A).subs(eq_F_in_mola, eq_F_in_molw).simplify_full()
385
units_check(eq_Foutv_Finv_Tout)
386
387
388
# In[32]:
389
390
eq_Foutv_Finv_Tout
391
392
393
# In[33]:
394
395
# Other way, using molar in and outflow
396
eq_Foutmolw_Finmolw_Elmol = F_out_molw == (F_in_molw + E_lmol*L_A)
397
units_check(eq_Foutmolw_Finmolw_Elmol)
398
399
400
# In[ ]:
401
402
403
404
405
# <h2>Change in air temperature</h2>
406
# <p>See also <a href="http://www.engineeringtoolbox.com/mixing-humid-air-d_694.html">http://www.engineeringtoolbox.com/mixing-humid-air-d_694.html</a> and <a href="http://www.engineeringtoolbox.com/enthalpy-moist-air-d_683.html">http://www.engineeringtoolbox.com/enthalpy-moist-air-d_683.html</a> for reference.</p>
407
# <p>We will assume that the air entering the chamber mixes with the air inside the chamber at constant pressure, i.e. the volume of the mixed air becomes the chamber volume plus the volume of the air that entered. The temperature of the mixed air is then the sum of their enthalpies plus the heat added by the fan and by sensible heaflux, divided by the sum of their heat capacities. The addition of water vapour through evaporation by itself should not affect the air temperature, but the volume of the air.</p>
408
# <p> </p>
409
# <p>Alternatively, we could assume that a given amount of air is added to a constant volume, leading to an increase in pressure. Addition of water vapour would lead to an additional increase in pressure. In addition, addition/removal of heat by sensible heat flux and the chamber fan would affect both temperature and pressure.To calculate both temperature and pressure, we need to track the internal energy in addition to the mole number. According to Eq. 6.1.3 in Kondepudi & Prigogine (2006), the internal energy of an ideal gas is given by (see also Eq. 2.2.15 in Kondepuid & Prigogine):</p>
410
# <p>$U = N(U_0 + C_v T)$</p>
411
# <p>where</p>
412
# <p>$U_0 = M c^2$</p>
413
# <p>The relation between molar heat capacities at constant pressure and volume is given as :</p>
414
# <p>$C_v = C_p - R_{mol}$</p>
415
# <p>Any heat exchanged by sensible heat flux, across the walls and the fan can be added to total $U$, and then knowledge about total $C_v$ will let us calculate air temperature inside the chamber. After that, we can use the ideal gas law to calculate volume or pressure, depending in which of those we fixed:</p>
416
# <p>$P V = n R T$</p>
417
# <p> </p>
418
# <p>The difference in water vapour pressure and temperature between the incoming and outgoing air is a function of the latent and sensible heat flux, as well as the flow rate. The heat fluxes associated with the incoming and outgoing air are $T_{in} (c_{pa} F_{in,mola} M_{air} + c_{pv} F_{in,molw} M_{w})$ and $T_{out} (c_{pa} F_{out,mola} M_{air} + c_{pv} F_{out,molw} M_{w})$ respectively. The difference between the two plus any additional heat sources/sinks ($Q_{in}$) equals the sensible heat flux at constant air temperature (steady state).</p>
419
420
# In[34]:
421
422
units_check(eq_F_out_v)
423
424
425
# In[35]:
426
427
var2('M_air', 'Molar mass of air (kg mol-1)', kilogram/mole, value = 0.02897, latexname='M_{air}') # http://www.engineeringtoolbox.com/molecular-mass-air-d_679.html
428
var2('c_pv', 'Specific heat of water vapour at 300 K', joule/kilogram/kelvin, latexname = 'c_{pv}', value = 1864) # source: http://www.engineeringtoolbox.com/water-vapor-d_979.html
429
var2('Q_in', 'Internal heat sources, such as fan', joule/second, latexname = 'Q_{in}')
430
eq_chamber_energy_balance = 0 == H_l*L_A + Q_in + T_in*(c_pa*M_air*F_in_mola + c_pv*M_w*F_in_molw) - (T_out*(c_pa*M_air*F_out_mola + c_pv*M_w*F_out_molw)); show(eq_chamber_energy_balance)
431
eq_Hl_enbal = solve(eq_chamber_energy_balance.subs(F_out_mola == F_in_mola, F_out_molw == F_in_molw + L_A*E_lmol), H_l)[0].expand()
432
print units_check(eq_Hl_enbal)
433
434
435
# In[36]:
436
437
soln = solve(eq_chamber_energy_balance.subs(eq_Foutmolw_Finmolw_Elmol, F_out_mola == F_in_mola), T_out)
438
print soln
439
eq_Tout_Finmol_Tin = soln[0]
440
units_check(eq_Tout_Finmol_Tin).simplify_full().convert().simplify_full()
441
442
443
# In[37]:
444
445
eq_Tout_Finv_Tin = eq_Tout_Finmol_Tin.subs(eq_F_in_mola, eq_F_in_molw).simplify_full()
446
print eq_Tout_Finv_Tin
447
show(eq_Tout_Finv_Tin)
448
units_check(eq_Tout_Finv_Tin).simplify_full().convert()
449
450
451
# In[ ]:
452
453
454
455
456
# <p>The molar outflux of dry air equals the molar influx of dry air, while the molar outflux of water vapour equals the molar influx plus the evaporation rate. The sum of both can be used to obtain the volumetric outflow:</p>
457
458
# In[38]:
459
460
# F_out_v as function of F_inv and T_in
461
eq1 = (eq_F_out_molw + eq_F_out_mola).simplify_full(); show(eq1)
462
eq2 = eq1.subs(F_out_mola == F_in_mola, eq_Foutmolw_Finmolw_Elmol).subs(eq_F_in_mola, eq_F_in_molw); show(eq2)
463
soln = solve(eq2,F_out_v); print soln
464
eq_Foutv_Finv = soln[0]; show(eq_Foutv_Finv)
465
units_check(eq_Foutv_Finv)
466
467
468
# In[39]:
469
470
eq_Foutv_Finv
471
472
473
# In[40]:
474
475
eq_Foutv_Finv_Tout
476
477
478
# In[41]:
479
480
eq_Tout_Finmol_Tin
481
482
483
# In[42]:
484
485
# Finding the T_in that would balance sensible heat release by the plate for given F_inv
486
assume(F_in_v > 0)
487
assume(F_out_v > 0)
488
assume(E_lmol >=0)
489
show(eq_chamber_energy_balance)
490
soln = solve(eq_chamber_energy_balance.subs(F_out_mola == F_in_mola).subs(eq_Foutmolw_Finmolw_Elmol).subs(eq_F_in_mola, eq_F_in_molw), T_in)
491
print soln
492
eq_T_in_ss = soln[0]
493
show(eq_T_in_ss)
494
495
496
# In[43]:
497
498
# Calculating Q_in from T_in, F_in_v and T_out
499
soln = solve(eq_T_in_ss,Q_in)
500
print soln
501
eq_Qin_Tin_Tout = soln[0]
502
503
504
# In[44]:
505
506
# Calculating H_l from T_in, F_in_v and T_out
507
soln = solve(eq_T_in_ss,H_l)
508
print soln
509
eq_Hl_Tin_Tout = soln[0]
510
eq_Hl_Tin_Tout.show()
511
512
513
# In[45]:
514
515
# Calculating H_l from T_in, T_out and Fmol
516
soln = solve(eq_chamber_energy_balance.subs(F_out_mola == F_in_mola), H_l)
517
print soln
518
eq_Hl_Tin_Tout_Fmol = soln[0].simplify_full()
519
eq_Hl_Tin_Tout_Fmol.show()
520
521
522
# <h2>Calculation of volumetric flow rate based on Cellkraft measurements</h2>
523
# <p>Cellcraft uses Arden-Buck equation to convert between vapour pressure and dew point (<a href="http://en.wikipedia.org/wiki/Arden_Buck_Equation">http://en.wikipedia.org/wiki/Arden_Buck_Equation</a>).<br />The air flow rate is given by the Cellkraft humidifier in l/min, but it refers to dry air at 0 $^o$C and 101300 Pa.</p>
524
# <p>We will use the reported dew point temperature to obtain the vapour pressure of the air coming out from the Cellkraft humidifier, then the ideal gas law to obtain the molar flow of dry air, leading to three equations with three unknowns:</p>
525
# <p>$F_{\mathit{in}_{\mathit{mola}}} = \frac{F_{\mathit{in}_{\mathit{va}_{n}}} P_{r}}{{R_{mol}} T_{r}}$</p>
526
# <p>$F_{\mathit{in}_{v}} = \frac{{\left(F_{\mathit{in}_{\mathit{mola}}} + F_{\mathit{in}_{\mathit{molw}}}\right)} {R_{mol}} T_{\mathit{in}}}{P_{a}}$</p>
527
# <p>$F_{\mathit{in}_{\mathit{molw}}} = \frac{F_{\mathit{in}_{v}} P_{v_{\mathit{in}}}}{{R_{mol}} T_{\mathit{in}}}$</p>
528
529
# In[46]:
530
531
# Calculating vapour pressure in the incoming air from reported dew point by the Cellkraft humidifier
532
var2('T0', 'Freezing point in kelvin',kelvin, value = 273.15)
533
eq_Pwin_Tdew = P_w_in == 611.21*exp((18.678 - (T_d - T0)/234.5)*((T_d - T0)/(257.14-T0+T_d))) # c
534
list_Tdew = np.array(srange(-40,50,6))+273.25
535
list_Pwin = [eq_Pwin_Tdew.rhs()(T_d = dummy).subs(cdict) for dummy in list_Tdew]
536
print list_Pwin
537
P = plot(eq_Pwin_Tdew.rhs().subs(cdict), (T_d, 253,303), frame = True, axes = False, legend_label = 'Arden Buck')
538
P += plot(eq_Pwl.rhs().subs(cdict), (T_l, 253,303), color = 'red', linestyle = '--', legend_label = 'Clausius-Clapeyron')
539
P += list_plot(zip(list_Tdew,list_Pwin), legend_label = 'Cellcraft')
540
P.axes_labels(['Dew point (K)', 'Saturation vapour pressure (Pa)'])
541
P
542
543
544
# In[47]:
545
546
eq_F_in_molw.show()
547
548
549
# In[48]:
550
551
eq_Finv_Finmol = F_in_v == (F_in_mola + F_in_molw)*R_mol*T_in/P_a
552
units_check(eq_Finv_Finmol)
553
554
555
# In[49]:
556
557
var2('F_in_va_n', 'Volumetric inflow of dry air at 0oC and 101325 Pa', meter^3/second, latexname='F_{in,v,a,n}')
558
var2('P_r', 'Reference pressure',pascal, value = 101325)
559
var2('T_r', 'Reference temperature', kelvin)
560
561
eq_Finmola_Finva_ref = fun_eq(F_in_mola == F_in_va_n * P_r/(R_mol*T_r))
562
563
564
# <p>To get $F_{in,v}$ and $F_{in,mol,w}$, we will consider that:</p>
565
# <p>$P_d = P_a - P_w$</p>
566
# <p>$P_w F_{in,v} = F_{in,mol,w} R_{mol} T_{in}$</p>
567
# <p>$(P_a - P_w) F_{in,v} = F_{in,mol,a} R_{mol} T_{a}$</p>
568
569
# In[50]:
570
571
eq_Finmolw_Finv = F_in_molw == (P_w_in*F_in_v)/(R_mol*T_in)
572
print units_check(eq_Finmolw_Finv)
573
eq_Finv_Finmola = F_in_v == F_in_mola*R_mol*T_in/(P_a - P_w_in)
574
print units_check(eq_Finv_Finmola)
575
eq_Finmolw_Finmola_Pwa = fun_eq(eq_Finmolw_Finv.subs(eq_Finv_Finmola))
576
eq_Finv_Finva_ref = fun_eq(eq_Finv_Finmola.subs(eq_Finmola_Finva_ref))
577
578
579
# In[51]:
580
581
vdict = cdict.copy()
582
vdict[F_in_va_n] = 10e-3/60 # 10 l/min reported by Cellkraft
583
vdict[T_d] = 273.15 + 10 # 10oC dew point
584
vdict[P_a] = 101325.
585
vdict[T_r] = 273.15
586
vdict[P_w_in] = eq_Pwin_Tdew.rhs().subs(vdict)
587
print vdict[P_w_in]
588
print vdict[F_in_va_n]
589
590
vdict[T_in] = 273.15+0
591
inflow0 = eq_Finv_Finva_ref.rhs().subs(vdict)
592
print 'Volumentric flow at 0 oC: ' + str(inflow0) + ' m3/s'
593
594
vdict[T_in] = 273.15+25
595
inflow25 = eq_Finv_Finva_ref.rhs().subs(vdict)
596
print 'Volumentric flow at 25 oC: ' + str(inflow25) + ' m3/s'
597
598
599
print '25oC/0oC: ' + str(inflow25/inflow0)
600
601
vdict[T_in] = 273.15+25
602
vdict[P_w_in] = 0.
603
inflow25 = eq_Finv_Finva_ref.rhs().subs(vdict)
604
print 'Volumentric flow at 25 oC without added vapour: ' + str(inflow25) + ' m3/s'
605
606
607
# <h2>Vapour pressure</h2>
608
# <p>The water fluxes associated with the incoming and the outgoing air according to the ideal gas law are $P_{v,in} F_{in,v}/(R_{mol} T_{in})$ and $P_{v,out}  F_{out,v}/(R_{mol} T_{out})$ respectively.</p>
609
610
# In[52]:
611
612
eq_Foutv_Finv_Tout.show()
613
614
615
# In[53]:
616
617
eq_F_out_v.show()
618
619
620
# In[54]:
621
622
eq_F_in_mola.subs(eq_Finv_Finva_ref).show()
623
624
625
# In[55]:
626
627
eq_Finv_Finva_ref.show()
628
629
630
# In[56]:
631
632
eq_F_in_molw.show()
633
eq_F_out_molw.show()
634
eq_Foutmolw_Finmolw_Elmol.show()
635
636
637
# In[57]:
638
639
eq1 = eq_F_out_molw.rhs() == eq_Foutmolw_Finmolw_Elmol.rhs().subs(eq_F_in_molw)
640
eq1.show()
641
soln = solve(eq1, P_w_out)
642
eq_Pwout_Elmol = fun_eq(soln[0].subs(eq_Foutv_Finv_Tout.subs(eq_F_in_molw)).simplify_full())
643
units_check(eq_Pwout_Elmol)
644
645
646
# <p><span style="color: #ff0000;">It is a bit surprising that steady-state $P_{w_{out}}$ does not depend on $T_{out}$.</span></p>
647
648
# In[58]:
649
650
soln[0].subs(eq_Foutv_Finv_Tout.subs(eq_F_in_molw)).simplify_full().show()
651
soln[0].subs(eq_F_out_v).subs(F_out_mola = F_in_mola, F_out_molw = F_in_molw + E_lmol*L_A).simplify_full().show()
652
653
654
# <p><span style="color: #ff0000;">The above are equivalent, because $F_{in,v} P_a = (F_{in,mol,a} + F_{in,mol,w}) R_{mol} T_{in}$<br /></span></p>
655
656
# In[59]:
657
658
eq_Pwout_Elmol.subs(eq_Finv_Finva_ref).simplify_full().show()
659
660
661
# In[60]:
662
663
show(soln[0])
664
show(eq_Foutv_Finv_Tout)
665
show(eq_F_in_molw)
666
667
668
# In[61]:
669
670
show(soln[0].subs(eq_Foutv_Finv_Tout.subs(eq_F_in_molw)).simplify())
671
672
673
# In[62]:
674
675
# T_out cancels out when the above is expanded
676
show(soln[0].subs(eq_Foutv_Finv_Tout.subs(eq_F_in_molw)).expand())
677
678
679
# <p>To convert from energetic to molar units, we need to divide $E_l$ by $\lambda_E M_w$:</p>
680
681
# In[63]:
682
683
eq_Elmol_El = E_lmol == E_l/(lambda_E*M_w)
684
print units_check(eq_Elmol_El)
685
eq_El_Elmol = E_l == E_lmol*lambda_E*M_w
686
687
688
# In[64]:
689
690
# In order to keep P_w_out = P_wa = const., we need to adjust P_w_in accordingly.
691
soln = solve(eq_Pwout_Elmol, P_w_in)
692
eq_Pwin_Elmol = soln[0].simplify_full()
693
show(eq_Pwin_Elmol)
694
695
696
# In[65]:
697
698
vdict = cdict.copy()
699
vdict[F_in_va_n] = 10e-3/60 # 10 l/min reported by Cellkraft
700
vdict[T_d] = 273.15 + 10 # 10oC dew point
701
vdict[P_a] = 101325.
702
vdict[T_r] = 273.15
703
vdict[P_w_in] = eq_Pwin_Tdew.rhs().subs(vdict)
704
print vdict[P_w_in]
705
print vdict[F_in_va_n]
706
707
vdict[T_in] = 273.15+0
708
inflow0 = eq_Finv_Finva_ref.rhs().subs(vdict)
709
print 'Volumentric flow at 0 oC: ' + str(inflow0) + ' m3/s'
710
711
vdict[T_in] = 273.15+25
712
inflow25 = eq_Finv_Finva_ref.rhs().subs(vdict)
713
vdict[F_in_v] = eq_Finv_Finva_ref.rhs().subs(vdict)
714
print 'Volumentric flow at 25 oC: ' + str(inflow25) + ' m3/s'
715
716
717
print '25oC/0oC: ' + str(inflow25/inflow0)
718
719
vdict[T_in] = 273.15+25
720
vdict[P_w_in] = 0.
721
inflow25 = eq_Finv_Finva_ref.rhs().subs(vdict)
722
print 'Volumentric flow at 25 oC without added vapour: ' + str(inflow25) + ' m3/s'
723
724
vdict[E_l] = 100. # assuming 100 W/m2 El
725
vdict[L_A] = 0.03^2
726
vdict[E_lmol] = eq_Elmol_El.rhs().subs(vdict)
727
vdict[T_out] = 273+20. # Assuming 20oC T in chamber
728
729
eq_Pwout_Elmol.subs(vdict)
730
731
732
# <h2>Net radiation measurement</h2>
733
# <p>According to Incropera_fundamentals, Table 13.1, the view factor (absorbed fraction of radiation emitted by another plate) of a small plate of width $w_i$ at a distance $L$ from a parallel larger plate of width $w_j$ is calculated as:</p>
734
735
# In[66]:
736
737
var2('L_s', 'Width of net radiation sensor', meter)
738
var2('L_ls', 'Distance between leaf and net radiation sensor', meter)
739
var2('F_s', 'Fraction of radiation emitted by leaf, absorbed by sensor', 1)
740
Wi = L_s/L_ls
741
Wj = L_l/L_ls
742
eq_Fs = F_s == (sqrt((Wi + Wj)^2 + 4) - sqrt((Wj - Wi)^2 + 4))/(2*Wi)
743
units_check(eq_Fs)
744
745
746
# In[67]:
747
748
vdict = cdict.copy()
749
vdict[L_l] = 0.03
750
vdict[L_s] = 0.01
751
print eq_Fs.rhs().subs(vdict)(L_ls = 0.01)
752
P = plot(eq_Fs.rhs().subs(vdict), (L_ls, 0.001, 0.02))
753
P.axes_labels(['Distance to leaf (m)', 'Fraction of radiation captured'])
754
P
755
756
757
# # Saving definitions to separate file
758
# In the below, we save the definitions and variables to separate files in the /temp directory, one with the extension .sage, from which we can selectively load functions using
759
# `%load fun_name filenam.sage`
760
# and one with the extension .sobj, to be loaded elsewhere using
761
# `load_session()`
762
763
# In[68]:
764
765
fun_export_ipynb('leaf_chamber_eqs', 'temp/')
766
save_session('temp/leaf_chamber_eqs.sobj')
767
768
769
# # Table of symbols
770
771
# In[69]:
772
773
# Creating dictionary to substitute names of units with shorter forms
774
var('m s J Pa K kg mol')
775
subsdict = {meter: m, second: s, joule: J, pascal: Pa, kelvin: K, kilogram: kg, mole: mol}
776
var('N_Re_L N_Re_c N_Le N_Nu_L N_Gr_L N_Sh_L')
777
dict_varnew = {Re: N_Re_L, Re_c: N_Re_c, Le: N_Le, Nu: N_Nu_L, Gr: N_Gr_L, Sh: N_Sh_L}
778
dict_varold = {v: k for k, v in dict_varnew.iteritems()}
779
variables = sorted([str(variable.subs(dict_varnew)) for variable in udict.keys()],key=str.lower)
780
tableheader = [('Variable', 'Description (value)', 'Units')]
781
tabledata = [('Variable', 'Description (value)', 'Units')]
782
for variable1 in variables:
783
variable2 = eval(variable1).subs(dict_varold)
784
variable = str(variable2)
785
tabledata.append((eval(variable),docdict[eval(variable)],fun_units_formatted(variable)))
786
787
table(tabledata, header_row=True)
788
789
790
# In[ ]:
791
792
793
794
795