Merge branch 'master' into devel
[wrffire.git] / wrfv2_fire / phys / module_cam_molec_diff.F
blobd8a2bd619751c87da1b3b5676c9410e93210401c
1 #define WRF_PORT
3 module molec_diff
4   
5   !------------------------------------------------------------------------------------------------- !
6   ! Module to compute molecular diffusivity for various constituents                                 !
7   !                                                                                                  !    
8   ! Public interfaces :                                                                              !
9   !                                                                                                  !
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                                  !
16   !                                                                                                  !
17   !---------------------------Code history---------------------------------------------------------- !
18   ! Modularized     :  J. McCaa, September 2004                                                      !
19   ! Lastly Arranged :  S. Park,  January.  2010                                                      !
20   !------------------------------------------------------------------------------------------------- !
21     
22 #ifndef WRF_PORT 
23     use perf_mod
24     use cam_logfile,  only : iulog
25 #else
26   use module_cam_support,   only: iulog, t_stopf, t_startf
27 #endif
29   implicit none
30   private       
31   save
33   public init_molec_diff
34 #ifndef WRF_PORT   
35   public init_timestep_molec_diff
36 #endif
37   public compute_molec_diff 
38   public vd_lu_qdecomp
40   ! ---------- !
41   ! Parameters ! 
42   ! ---------- !
44   integer,  parameter   :: r8 = selected_real_kind(12) ! 8 byte real
45   
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 ]
56   real(r8)              :: gravit     
57   real(r8)              :: cpair
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 ? ]
63   
64   contains
66   !============================================================================ !
67   !                                                                             !
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 )
72     
73     use constituents,     only : cnst_mw
74     use upper_bc,         only : ubc_init
75     
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
87     
88     if( kind .ne. r8 ) then
89         write(iulog,*) 'KIND of reals passed to init_molec_diff -- exiting.'
90         stop 'init_molec_diff'
91     endif
92     
93     rair       = rair_in
94     mw_dry     = mw_dry_in
95     n_avog     = n_avog_in
96     gravit     = gravit_in
97     cpair      = cpair_in
98     kbtz       = kbtz_in
99     ntop_molec = ntop_molec_in
100     nbot_molec = nbot_molec_in
101     
102   ! Initialize upper boundary condition variables
104     call ubc_init()
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))
110     do m = 1, ncnst
111        mw_fac(m) = d0 * mw_dry * sqrt(1._r8/mw_dry + 1._r8/cnst_mw(m)) / n_avog
112     end do
114   end subroutine init_molec_diff
116   !============================================================================ !
117   !                                                                             !
118   !============================================================================ !
119 #ifndef WRF_PORT 
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)
131     
132   end subroutine init_timestep_molec_diff
133 #endif
134   !============================================================================ !
135   !                                                                             !
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 )
143     
144     use upper_bc,        only : ubc_get_vals
145     use constituents,    only : cnst_mw, cnst_fixed_ubc
147     ! --------------------- !
148     ! Input-Output Argument !
149     ! --------------------- !
150     
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
161     
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   
179     ! --------------- ! 
180     ! Local variables !
181     ! --------------- !
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
204     
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) )
212     
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
218        do i = 1, ncol
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)
223        end do
224     end do
225     kq_scal(:ncol,nbot_molec+1:pver+1) = 0._r8
226     
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
233     do i = 1, ncol
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) ) )
236     enddo
237     cd_top(:ncol) = cc_top(:ncol) * dse_top(:ncol)
238     
239     compute_molec_diff = 1
240     return
241   end function compute_molec_diff
243   !============================================================================ !
244   !                                                                             !
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
288     ! --------------- !
289     ! Local Variables !
290     ! --------------- !
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')
315     kvq(:,:)  = 0._r8
316     cd_top(:) = 0._r8
318   ! Compute difference between scale heights of constituent and dry air
320     do k = ntop_molec, nbot_molec
321        do i = 1, ncol
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) 
324        enddo
325     enddo
327     !-------------------- !
328     ! Molecular diffusion !
329     !-------------------- !
331     do k = nbot_molec - 1, ntop_molec, -1
332        kp1 = k + 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)
340     end do
342     if( fixed_ubc ) then
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)
346     end if
348   ! Calculate diagonal elements
350     do k = nbot_molec - 1, ntop_molec + 1, -1
351        kp1 = k + 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)
356     end do
358     k   = ntop_molec
359     kp1 = k + 1
360     if( fixed_ubc ) then
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)
366     else
367         cb(:ncol,k) = 1._r8 + ca(:ncol,k) &
368                     + rpdel(:ncol,k) * kvq(:ncol,kp1) * rghd(:ncol,kp1)
369     end if
371     k   = nbot_molec
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)
376     end do
378   ! Compute term for updating top level mixing ratio for ubc
380     if( fixed_ubc ) then
381         cd_top(:ncol) = cc(:ncol,ntop_molec) * ubc_mmr(:ncol)
382     end if
384     !-------------------------------------------------------- !
385     ! Calculate e(k).                                         !
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)
393     end do
394     k = ntop_molec
395     dnom(:ncol,k) = 1._r8 / ( cb(:ncol,k) - ca(:ncol,k) * ze(:ncol,k+1) )
397     vd_lu_qdecomp = 1
398     call t_stopf('vd_lu_qdecomp')
399     return
401   end function vd_lu_qdecomp
403   end module molec_diff