5 !------------------------------------------------------------------------------------------------- !
6 ! Module to compute molecular diffusivity for various constituents !
8 ! Public interfaces : !
10 ! init_molec_diff Initializes time independent coefficients !
11 ! init_timestep_molec_diff Time-step initialization for molecular diffusivity !
12 ! compute_molec_diff Computes constituent-independent terms for moleculuar diffusivity !
13 ! vd_lu_qdecomp Computes constituent-dependent terms for moleculuar diffusivity and !
14 ! updates terms in the triadiagonal matrix used for the implicit !
15 ! solution of the diffusion equation !
17 !---------------------------Code history---------------------------------------------------------- !
18 ! Modularized : J. McCaa, September 2004 !
19 ! Lastly Arranged : S. Park, January. 2010 !
20 !------------------------------------------------------------------------------------------------- !
24 use cam_logfile, only : iulog
26 use module_cam_support, only: iulog, t_stopf, t_startf
33 public init_molec_diff
35 public init_timestep_molec_diff
37 public compute_molec_diff
44 integer, parameter :: r8 = selected_real_kind(12) ! 8 byte real
46 real(r8), parameter :: km_fac = 3.55E-7_r8 ! Molecular viscosity constant [ unit ? ]
47 real(r8), parameter :: pr_num = 1._r8 ! Prandtl number [ no unit ]
48 real(r8), parameter :: pwr = 2._r8/3._r8 ! Exponentiation factor [ unit ? ]
49 real(r8), parameter :: d0 = 1.52E20_r8 ! Diffusion factor [ m-1 s-1 ] molec sqrt(kg/kmol/K) [ unit ? ]
50 ! Aerononmy, Part B, Banks and Kockarts (1973), p39
51 ! Note text cites 1.52E18 cm-1 ...
53 real(r8) :: rair ! Gas constant for dry air
54 real(r8) :: mw_dry ! Molecular weight of dry air
55 real(r8) :: n_avog ! Avogadro's number [ molec/kmol ]
58 real(r8) :: kbtz ! Boltzman constant
60 integer :: ntop_molec ! Top interface level to which molecular vertical diffusion is applied ( = 1 )
61 integer :: nbot_molec ! Bottom interface level to which molecular vertical diffusion is applied ( = pver )
62 real(r8), allocatable :: mw_fac(:) ! sqrt(1/M_q + 1/M_d) in constituent diffusivity [ unit ? ]
66 !============================================================================ !
68 !============================================================================ !
70 subroutine init_molec_diff( kind, ncnst, rair_in, ntop_molec_in, nbot_molec_in, &
71 mw_dry_in, n_avog_in, gravit_in, cpair_in, kbtz_in )
73 use constituents, only : cnst_mw
74 use upper_bc, only : ubc_init
76 integer, intent(in) :: kind ! Kind of reals being passed in
77 integer, intent(in) :: ncnst ! Number of constituents
78 integer, intent(in) :: ntop_molec_in ! Top interface level to which molecular vertical diffusion is applied ( = 1 )
79 integer, intent(in) :: nbot_molec_in ! Bottom interface level to which molecular vertical diffusion is applied.
80 real(r8), intent(in) :: rair_in
81 real(r8), intent(in) :: mw_dry_in ! Molecular weight of dry air
82 real(r8), intent(in) :: n_avog_in ! Avogadro's number [ molec/kmol ]
83 real(r8), intent(in) :: gravit_in
84 real(r8), intent(in) :: cpair_in
85 real(r8), intent(in) :: kbtz_in ! Boltzman constant
86 integer :: m ! Constituent index
88 if( kind .ne. r8 ) then
89 write(iulog,*) 'KIND of reals passed to init_molec_diff -- exiting.'
90 stop 'init_molec_diff'
99 ntop_molec = ntop_molec_in
100 nbot_molec = nbot_molec_in
102 ! Initialize upper boundary condition variables
106 ! Molecular weight factor in constitutent diffusivity
107 ! ***** FAKE THIS FOR NOW USING MOLECULAR WEIGHT OF DRY AIR FOR ALL TRACERS ****
109 allocate(mw_fac(ncnst))
111 mw_fac(m) = d0 * mw_dry * sqrt(1._r8/mw_dry + 1._r8/cnst_mw(m)) / n_avog
114 end subroutine init_molec_diff
116 !============================================================================ !
118 !============================================================================ !
120 subroutine init_timestep_molec_diff(state)
121 !--------------------------- !
122 ! Timestep dependent setting !
123 !--------------------------- !
124 use upper_bc, only : ubc_timestep_init
125 use physics_types,only: physics_state
126 use ppgrid, only: begchunk, endchunk
128 type(physics_state), intent(in) :: state(begchunk:endchunk)
130 call ubc_timestep_init( state)
132 end subroutine init_timestep_molec_diff
134 !============================================================================ !
136 !============================================================================ !
138 integer function compute_molec_diff( lchnk , &
139 pcols , pver , ncnst , ncol , t , pmid , pint , &
140 zi , ztodt , kvh , kvm , tint , rhoi , tmpi2 , &
141 kq_scal , ubc_t , ubc_mmr , dse_top , cc_top , cd_top , cnst_mw_out , &
142 cnst_fixed_ubc_out , mw_fac_out , ntop_molec_out , nbot_molec_out )
144 use upper_bc, only : ubc_get_vals
145 use constituents, only : cnst_mw, cnst_fixed_ubc
147 ! --------------------- !
148 ! Input-Output Argument !
149 ! --------------------- !
151 integer, intent(in) :: pcols
152 integer, intent(in) :: pver
153 integer, intent(in) :: ncnst
154 integer, intent(in) :: ncol ! Number of atmospheric columns
155 integer, intent(in) :: lchnk ! Chunk identifier
156 real(r8), intent(in) :: t(pcols,pver) ! Temperature input
157 real(r8), intent(in) :: pmid(pcols,pver) ! Midpoint pressures
158 real(r8), intent(in) :: pint(pcols,pver+1) ! Interface pressures
159 real(r8), intent(in) :: zi(pcols,pver+1) ! Interface heights
160 real(r8), intent(in) :: ztodt ! 2 delta-t
162 real(r8), intent(inout) :: kvh(pcols,pver+1) ! Diffusivity for heat
163 real(r8), intent(inout) :: kvm(pcols,pver+1) ! Viscosity ( diffusivity for momentum )
164 real(r8), intent(inout) :: tint(pcols,pver+1) ! Interface temperature
165 real(r8), intent(inout) :: rhoi(pcols,pver+1) ! Density ( rho ) at interfaces
166 real(r8), intent(inout) :: tmpi2(pcols,pver+1) ! dt*(g*rho)**2/dp at interfaces
168 real(r8), intent(out) :: kq_scal(pcols,pver+1) ! kq_fac*sqrt(T)*m_d/rho for molecular diffusivity
169 real(r8), intent(out) :: ubc_mmr(pcols,ncnst) ! Upper boundary mixing ratios [ kg/kg ]
170 real(r8), intent(out) :: cnst_mw_out(ncnst)
171 logical, intent(out) :: cnst_fixed_ubc_out(ncnst)
172 real(r8), intent(out) :: mw_fac_out(ncnst)
173 real(r8), intent(out) :: dse_top(pcols) ! dse on top boundary
174 real(r8), intent(out) :: cc_top(pcols) ! Lower diagonal at top interface
175 real(r8), intent(out) :: cd_top(pcols) ! cc_top * dse ubc value
176 integer, intent(out) :: ntop_molec_out
177 integer, intent(out) :: nbot_molec_out
183 integer :: m ! Constituent index
184 integer :: i ! Column index
185 integer :: k ! Level index
186 real(r8) :: mkvisc ! Molecular kinematic viscosity c*tint**(2/3)/rho
187 real(r8) :: ubc_t(pcols) ! Upper boundary temperature (K)
189 ! ----------------------- !
190 ! Main Computation Begins !
191 ! ----------------------- !
193 ! Get upper boundary values
195 call ubc_get_vals( lchnk, ncol, ntop_molec, pint, zi, ubc_t, ubc_mmr )
197 ! Below are already computed, just need to be copied for output
199 cnst_mw_out(:ncnst) = cnst_mw(:ncnst)
200 cnst_fixed_ubc_out(:ncnst) = cnst_fixed_ubc(:ncnst)
201 mw_fac_out(:ncnst) = mw_fac(:ncnst)
202 ntop_molec_out = ntop_molec
203 nbot_molec_out = nbot_molec
205 ! Density and related factors for moecular diffusion and ubc.
206 ! Always have a fixed upper boundary T if molecular diffusion is active. Why ?
208 tint (:ncol,ntop_molec) = ubc_t(:ncol)
209 rhoi (:ncol,ntop_molec) = pint(:ncol,ntop_molec) / ( rair * tint(:ncol,ntop_molec) )
210 tmpi2(:ncol,ntop_molec) = ztodt * ( gravit * rhoi(:ncol,ntop_molec))**2 &
211 / ( pmid(:ncol,ntop_molec) - pint(:ncol,ntop_molec) )
213 ! Compute molecular kinematic viscosity, heat diffusivity and factor for constituent diffusivity
214 ! This is a key part of the code.
216 kq_scal(:ncol,1:ntop_molec-1) = 0._r8
217 do k = ntop_molec, nbot_molec
219 mkvisc = km_fac * tint(i,k)**pwr / rhoi(i,k)
220 kvm(i,k) = kvm(i,k) + mkvisc
221 kvh(i,k) = kvh(i,k) + mkvisc * pr_num
222 kq_scal(i,k) = sqrt(tint(i,k)) / rhoi(i,k)
225 kq_scal(:ncol,nbot_molec+1:pver+1) = 0._r8
227 ! Top boundary condition for dry static energy
229 dse_top(:ncol) = cpair * tint(:ncol,ntop_molec) + gravit * zi(:ncol,ntop_molec)
231 ! Top value of cc for dry static energy
234 cc_top(i) = ztodt * gravit**2 * rhoi(i,ntop_molec) * km_fac * ubc_t(i)**pwr / &
235 ( ( pint(i,2) - pint(i,1) ) * ( pmid(i,1) - pint(i,1) ) )
237 cd_top(:ncol) = cc_top(:ncol) * dse_top(:ncol)
239 compute_molec_diff = 1
241 end function compute_molec_diff
243 !============================================================================ !
245 !============================================================================ !
247 integer function vd_lu_qdecomp( pcols , pver , ncol , fixed_ubc , mw , ubc_mmr , &
248 kv , kq_scal, mw_facm , tmpi , rpdel , &
249 ca , cc , dnom , ze , rhoi , &
250 tint , ztodt , ntop_molec , nbot_molec , cd_top )
252 !------------------------------------------------------------------------------ !
253 ! Add the molecular diffusivity to the turbulent diffusivity for a consitutent. !
254 ! Update the superdiagonal (ca(k)), diagonal (cb(k)) and subdiagonal (cc(k)) !
255 ! coefficients of the tridiagonal diffusion matrix, also ze and denominator. !
256 !------------------------------------------------------------------------------ !
258 ! ---------------------- !
259 ! Input-Output Arguments !
260 ! ---------------------- !
262 integer, intent(in) :: pcols
263 integer, intent(in) :: pver
264 integer, intent(in) :: ncol ! Number of atmospheric columns
266 integer, intent(in) :: ntop_molec
267 integer, intent(in) :: nbot_molec
269 logical, intent(in) :: fixed_ubc ! Fixed upper boundary condition flag
270 real(r8), intent(in) :: kv(pcols,pver+1) ! Eddy diffusivity
271 real(r8), intent(in) :: kq_scal(pcols,pver+1) ! Molecular diffusivity ( kq_fac*sqrt(T)*m_d/rho )
272 real(r8), intent(in) :: mw ! Molecular weight for this constituent
273 real(r8), intent(in) :: ubc_mmr(pcols) ! Upper boundary mixing ratios [ kg/kg ]
274 real(r8), intent(in) :: mw_facm ! sqrt(1/M_q + 1/M_d) for this constituent
275 real(r8), intent(in) :: tmpi(pcols,pver+1) ! dt*(g/R)**2/dp*pi(k+1)/(.5*(tm(k+1)+tm(k))**2
276 real(r8), intent(in) :: rpdel(pcols,pver) ! 1./pdel ( thickness bet interfaces )
277 real(r8), intent(in) :: rhoi(pcols,pver+1) ! Density at interfaces [ kg/m3 ]
278 real(r8), intent(in) :: tint(pcols,pver+1) ! Interface temperature [ K ]
279 real(r8), intent(in) :: ztodt ! 2 delta-t [ s ]
281 real(r8), intent(inout) :: ca(pcols,pver) ! -Upper diagonal
282 real(r8), intent(inout) :: cc(pcols,pver) ! -Lower diagonal
283 real(r8), intent(inout) :: dnom(pcols,pver) ! 1./(1. + ca(k) + cc(k) - cc(k)*ze(k-1)) , 1./(b(k) - c(k)*e(k-1))
284 real(r8), intent(inout) :: ze(pcols,pver) ! Term in tri-diag. matrix system
286 real(r8), intent(out) :: cd_top(pcols) ! Term for updating top level with ubc
292 integer :: i ! Longitude index
293 integer :: k, kp1 ! Vertical indicies
295 real(r8) :: rghd(pcols,pver+1) ! (1/H_i - 1/H) * (rho*g)^(-1)
296 real(r8) :: kmq(ncol) ! Molecular diffusivity for constituent
297 real(r8) :: wrk0(ncol) ! Work variable
298 real(r8) :: wrk1(ncol) ! Work variable
300 real(r8) :: cb(pcols,pver) ! - Diagonal
301 real(r8) :: kvq(pcols,pver+1) ! Output vertical diffusion coefficient
303 ! ----------------------- !
304 ! Main Computation Begins !
305 ! ----------------------- !
307 ! --------------------------------------------------------------------- !
308 ! Determine superdiagonal (ca(k)) and subdiagonal (cc(k)) coeffs of the !
309 ! tridiagonal diffusion matrix. The diagonal elements (cb=1+ca+cc) are !
310 ! a combination of ca and cc; they are not required by the solver. !
311 !---------------------------------------------------------------------- !
313 call t_startf('vd_lu_qdecomp')
318 ! Compute difference between scale heights of constituent and dry air
320 do k = ntop_molec, nbot_molec
322 rghd(i,k) = gravit / ( kbtz * n_avog * tint(i,k) ) * ( mw - mw_dry )
323 rghd(i,k) = ztodt * gravit * rhoi(i,k) * rghd(i,k)
327 !-------------------- !
328 ! Molecular diffusion !
329 !-------------------- !
331 do k = nbot_molec - 1, ntop_molec, -1
333 kmq(:ncol) = kq_scal(:ncol,kp1) * mw_facm
334 wrk0(:ncol) = ( kv(:ncol,kp1) + kmq(:ncol) ) * tmpi(:ncol,kp1)
335 wrk1(:ncol) = kmq(:ncol) * 0.5_r8 * rghd(:ncol,kp1)
336 ! Add species separation term
337 ca(:ncol,k ) = ( wrk0(:ncol) - wrk1(:ncol) ) * rpdel(:ncol,k)
338 cc(:ncol,kp1) = ( wrk0(:ncol) + wrk1(:ncol) ) * rpdel(:ncol,kp1)
339 kvq(:ncol,kp1) = kmq(:ncol)
343 cc(:ncol,ntop_molec) = kq_scal(:ncol,ntop_molec) * mw_facm &
344 * ( tmpi(:ncol,ntop_molec) + rghd(:ncol,ntop_molec) ) &
345 * rpdel(:ncol,ntop_molec)
348 ! Calculate diagonal elements
350 do k = nbot_molec - 1, ntop_molec + 1, -1
352 cb(:ncol,k) = 1._r8 + ca(:ncol,k) + cc(:ncol,k) &
353 + rpdel(:ncol,k) * ( kvq(:ncol,kp1) * rghd(:ncol,kp1) &
354 - kvq(:ncol,k) * rghd(:ncol,k) )
355 kvq(:ncol,kp1) = kv(:ncol,kp1) + kvq(:ncol,kp1)
361 cb(:ncol,k) = 1._r8 + ca(:ncol,k) &
362 + rpdel(:ncol,k) * kvq(:ncol,kp1) * rghd(:ncol,kp1) &
363 + kq_scal(:ncol,ntop_molec) * mw_facm &
364 * ( tmpi(:ncol,ntop_molec) - rghd(:ncol,ntop_molec) ) &
365 * rpdel(:ncol,ntop_molec)
367 cb(:ncol,k) = 1._r8 + ca(:ncol,k) &
368 + rpdel(:ncol,k) * kvq(:ncol,kp1) * rghd(:ncol,kp1)
372 cb(:ncol,k) = 1._r8 + cc(:ncol,k) + ca(:ncol,k) &
373 - rpdel(:ncol,k) * kvq(:ncol,k)*rghd(:ncol,k)
374 do k = 1, nbot_molec + 1, -1
375 cb(:ncol,k) = 1._r8 + ca(:ncol,k) + cc(:ncol,k)
378 ! Compute term for updating top level mixing ratio for ubc
381 cd_top(:ncol) = cc(:ncol,ntop_molec) * ubc_mmr(:ncol)
384 !-------------------------------------------------------- !
386 ! This term is required in solution of tridiagonal matrix !
387 ! defined by implicit diffusion equation. !
388 !-------------------------------------------------------- !
390 do k = nbot_molec, ntop_molec + 1, -1
391 dnom(:ncol,k) = 1._r8 / ( cb(:ncol,k) - ca(:ncol,k) * ze(:ncol,k+1) )
392 ze(:ncol,k) = cc(:ncol,k) * dnom(:ncol,k)
395 dnom(:ncol,k) = 1._r8 / ( cb(:ncol,k) - ca(:ncol,k) * ze(:ncol,k+1) )
398 call t_stopf('vd_lu_qdecomp')
401 end function vd_lu_qdecomp
403 end module molec_diff