module tracers_suite 6,4 !--------------------------------------------------------------- ! ! Implements artificial suite of tracers ! 1) low tracer with unit mixing ratio at about 800 mb ! 2) med tracer with unit mixing ratio at about 500 mb ! 3) high tracer with unit mixing ratio at about 200 mb ! 4) reverse med tracer with unit mixing ratio everywhere except about 500 mb ! 5) unit tracer with unit mixing ratio everywhere ! ! D Bundy June 2003 ! modified Feb 2004 to include TT_UN and smoothing ! ! A Mirin and B Eaton, August 2007 ! Modified to create up to 1000 distinct copies of the 5 basic tracers ! by appending up to a 3 digit number to the base tracer name. ! RESTRICTION - trac_ncnstmx cannot exceed 5000 unless the algorithm for ! constructing new tracer names is extended. ! ! This is a module that contains a suite of tracers that is interfaced ! by tracers.F90. The details of the suite are contained entirely ! in this file, so the public routines are all very generic. ! ! ------------ calling tree -------------- ! Initialize the tracer mixing ratio field ! -> tracers.F90: tracers_init_cnst ! -> tracers_suite.F90:init_cnst_tr ! -> init_cnst_lw ! -> init_cnst_md ! -> init_cnst_hi ! -> init_cnst_un ! ! Initialize data set, things that need to be done at the beginning of a ! run (whether restart or initial) ! -> tracers.F90: tracers_init ! -> tracers_suite.F90:init_tr ! ! Timestepping: ! -> tracers_timestep_init ! -> tracers_suite.F90:timestep_init_tr ! ! -> tracers_timestep_tend ! -> tracers_suite.F90:flux_tr ! -> flux_lw ! -> flux_md ! -> flux_hi ! -> flux_un ! ! -> tracers_suite.F90:tend_tr ! -> tend_lw ! -> tend_md ! -> tend_hi ! -> tend_un ! !--------------------------------------------------------------- use shr_kind_mod, only: r8 => shr_kind_r8 use ppgrid, only: pcols, pver use abortutils, only: endrun use cam_logfile, only: iulog implicit none private save ! Public interfaces public get_tracer_name ! store names of tracers public init_cnst_tr ! initialize tracer fields public init_tr ! initialize data sets need for tracer public tend_tr ! tracer tendency public flux_tr ! surface flux of tracer public timestep_init_tr ! interpolate tracer emissions data set ! Private module data integer, parameter :: trac_ncnstmx=5000 ! Max no. of tracers based on current algorithm for ! constructing tracer names. This could easily be extended. integer, parameter :: trac_names=5 ! No. of base tracers logical, parameter :: smooth = .false. contains !====================================================================== function get_tracer_name(n) 4,1 !------------------------------------------------------------------------ ! Purpose: ! ! The tracer names are only defined in this module. This function is for ! outside programs to grab the name for each tracer number. ! If n > trac_ncst calls endrun. ! !------------------------------------------------------------------------ ! -----------------------------Arguments--------------------------------- integer, intent(in) :: n character(len=8) :: get_tracer_name ! return value ! ----------------------------- Local --------------------------------- character(len=5), dimension(trac_names), parameter :: & ! constituent names tracer_names = (/ 'TT_LW', 'TT_MD', 'TT_HI', 'TTRMD' , 'TT_UN'/) !----------------------------------------------------------------------- integer :: nbase ! Corresponding base tracer index integer :: ncopy ! No. of copies of base tracers character(len=1) :: c1 character(len=2) :: c2 character(len=3) :: c3 if ( n > trac_ncnstmx ) then write(iulog,*) 'tracers_suite:get_tracer_name()','requested tracer',n write(iulog,*) 'only ',trac_ncnstmx,' tracers available' call endrun else nbase = mod(n-1, trac_names) + 1 ncopy = (n-1)/trac_names if ( ncopy == 0 ) then get_tracer_name = tracer_names(nbase) else if ( ncopy >= 1 .and. ncopy <= 9 ) then write (c1,'(i1)') ncopy get_tracer_name = tracer_names(nbase) // c1 else if ( ncopy >= 10 .and. ncopy <= 99 ) then write (c2,'(i2)') ncopy get_tracer_name = tracer_names(nbase) // c2 else if ( ncopy >= 100 .and. ncopy <= 999 ) then write (c3,'(i3)') ncopy get_tracer_name = tracer_names(nbase) // c3 end if endif return end function get_tracer_name !====================================================================== !====================================================================== subroutine init_cnst_tr(m,q, gcid) 1,7 !----------------------------------------------------------------------- ! ! Purpose: ! calls initialization routine for tracer m, returns mixing ratio in q ! ! This routine must be consistent with trac_ncnstmx. ! !----------------------------------------------------------------------- implicit none real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air (gcol, plev) integer ,intent(in) :: m ! index of tracer integer, intent(in) :: gcid(:) ! global column id integer nbase ! Corresponding base tracer index if ( m > trac_ncnstmx ) then write(iulog,*) 'tracers_suite:init_cnst_tr()' write(iulog,*) ' asked to initialize tracer number ',m write(iulog,*) ' but there are only trac_ncnstmx = ',trac_ncnstmx,' tracers' call endrun endif nbase = mod(m-1,trac_names)+1 if ( nbase == 1 ) then call init_cnst_lw(q, gcid) else if ( nbase == 2 ) then call init_cnst_md(q, gcid) else if ( nbase == 3 ) then call init_cnst_hi(q, gcid) else if ( nbase == 4 ) then call init_cnst_md(q, gcid, rev_in=1) else if ( nbase == 5 ) then call init_cnst_un(q, gcid) else write(iulog,*) 'tracers_suite:init_cnst_tr()' write(iulog,*) 'no initialization routine specified for tracer',nbase call endrun endif end subroutine init_cnst_tr !====================================================================== subroutine init_cnst_lw(q, gcid) 1,2 !----------------------------------------------------------------------- ! ! Purpose: ! Initialize test tracer 1: low ! !----------------------------------------------------------------------- implicit none !Arguments real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air (gcol,plev) integer, intent(in) :: gcid(:) ! global column id ! Local integer indx !----------------------------------------------------------------------- ! ! Initialize low tracer to zero except at 800 level ! indx = setpindxtr(800._r8) if ( smooth ) then call setsmoothtr(indx,q,.876_r8) else q = 0.0_r8 q(:,indx) = 1.0_r8 endif ! write(iulog,*)'suite 1 init_cnst_lw min/max q',minval(q),maxval(q) end subroutine init_cnst_lw !====================================================================== subroutine init_cnst_md(q,gcid,rev_in) 2,2 !----------------------------------------------------------------------- ! ! Purpose: ! Initialize test tracer 2: med ! !----------------------------------------------------------------------- implicit none !Arguments real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air integer, intent(in) :: gcid(:) ! global column id integer, intent(in), optional :: rev_in ! reverse the mixing ratio ! Local integer indx integer rev !----------------------------------------------------------------------- ! ! Initialize med tracer to zero except at 500 level ! rev = 0 if (present(rev_in)) then if (rev_in == 1) then rev = 1 endif endif indx = setpindxtr(500._r8) if ( smooth ) then call setsmoothtr(indx,q,.876_r8,rev_in=rev) else if (rev == 1 ) then q = 1.0_r8 q(:,indx) = 0.0_r8 else q = 0.0_r8 q(:,indx) = 1.0_r8 endif endif ! write(iulog,*)'suite 1 init_cnst_md min/max q',minval(q),maxval(q) end subroutine init_cnst_md !====================================================================== subroutine init_cnst_hi(q, gcid) 1,2 !----------------------------------------------------------------------- ! ! Purpose: ! Initialize test tracer 3: high ! !----------------------------------------------------------------------- implicit none !Arguments real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air integer, intent(in) :: gcid(:) ! global column id ! Local integer indx !----------------------------------------------------------------------- ! ! Initialize high tracer to zero except at 200 level ! indx = setpindxtr(200._r8) if ( smooth ) then call setsmoothtr(indx,q,.3_r8) else q = 0.0_r8 q(:,indx) = 1.0_r8 endif ! write(iulog,*)'suite 1 init_cnst_hi min/max q',minval(q),maxval(q) end subroutine init_cnst_hi !====================================================================== subroutine init_cnst_un(q, gcid) 1 !----------------------------------------------------------------------- ! ! Purpose: ! Initialize test unit tracer. ! 2) conserved unit tracer ! ! Original version: B. Eaton, 1995 ! Standardized: T. Acker, Feb 1996 ! copied from tracers.F90:initesttr() D.Bundy Oct 15 2002 !----------------------------------------------------------------------- implicit none real(r8), intent(out) :: q(:,:) ! kg tracer/kg dry air integer, intent(in) :: gcid(:) ! global column id !----------------------------------------------------------------------- ! Initialize conserved unit tracer. q = 1.0_r8 end subroutine init_cnst_un !====================================================================== subroutine init_tr 1 !----------------------------------------------------------------------- ! Purpose: ! Initialize any datasets that the constituents need. ! Currently does nothing for this suite ! D Bundy, May 30, 2003 !----------------------------------------------------------------------- end subroutine init_tr !====================================================================== subroutine timestep_init_tr 1 !----------------------------------------------------------------------- ! ! Purpose: call tracer timestep init processes !----------------------------------------------------------------------- end subroutine timestep_init_tr !====================================================================== subroutine setsmoothtr(indx,q,width,rev_in) 3,1 use hycoef, only : hypm implicit none !Arguments integer, intent(in) :: indx ! k index of pressure level real(r8), intent(inout) :: q(:,:) ! kg tracer/kg dry air real(r8), intent(in) :: width ! eta difference from unit level where q = 0.1 integer, intent(in), optional :: rev_in ! reverse the mixing ratio !Local variables integer k real(r8) alpha ! guassian width, determined by width, T real(r8) pdist ! pressure distance (eta.e4) from k=indx real(r8) T ! desired m.r. in level specified by pdiff from k=indx integer rev ! = 1 then reverse (q = 1, q(k=indx) = 0 ) rev = 0 if (present(rev_in)) then if (rev_in == 1) then rev = 1 endif endif ! write(iulog,*)'TR SMOOTH indx hypm(indx)',indx,hypm(indx) ! write(iulog,67)'TR SMOOTH ','k','hypm(k)','pdist','-a*(pd^2)','e^-a*(pd^2)' T = 0.1_r8 alpha = -log(T)/(width*1.e4_r8)**2 ! s.t. in level width from indx, mr = T ! alpha = 3.e-8 ! m.r. ~ 0.1 in adjacent levels, where change eta ~ 0.08 do k=1,pver pdist = hypm(k) - hypm(indx) if ( rev == 1 ) then q(:,k) = 1.0_r8 - exp(-alpha*(pdist**2)) else q(:,k) = exp(-alpha*(pdist**2)) endif ! write(iulog,66)'TR SMOOTH ',k,hypm(k),pdist,-alpha*pdist**2,q(1,k) end do 66 format (a15,i4,3f15.2,g15.6) 67 format (a15,a4,4a15) end subroutine setsmoothtr !====================================================================== integer function setpindxtr(pmb) 3,1 ! find the index of layer nearest pmb use hycoef, only : hypm implicit none real(r8) pmb real(r8) pmin, pdist integer indx, k indx = 0 pmin = 1.e36_r8 pdist = 1.e36_r8 do k=1,pver pdist = abs(hypm(k) - pmb*100._r8) if (pdist < pmin) then indx = k pmin = pdist end if end do write(iulog,*) ' index near', pmb, ' is ', indx setpindxtr = indx end function setpindxtr !====================================================================== !====================================================================== subroutine tend_tr(m,ncol, q, deltat, snk) 1,7 !----------------------------------------------------------------------- ! ! Purpose: Calculate tendency (decay) of radon tracer ! ! Method: !-------------------------Code History---------------------------------- ! ! tracers.F90:rndecay() ! Original version: B. Eaton, 1995 ! Standardized: T. Acker, Feb 1996 ! ! !----------------------------------------------------------------------- implicit none !-------------------------Arguments-------------------------------------- ! integer, intent(in) :: m ! tracer number integer, intent(in) :: ncol ! number of atmospheric columns real(r8), intent(in) :: q(pcols,pver) ! radon mixing ratio (kg/(kg moist air)) real(r8), intent(in) :: deltat ! time step real(r8), intent(out) :: snk(pcols,pver) ! conversion rate ! (kg tracer /(s kg moist air)) !--------------------------Local Variables------------------------------ integer nbase if ( m > trac_ncnstmx ) then write(iulog,*) 'tracers_suite:tend_tr()' write(iulog,*) ' asked to calculate tendency for tracer number ',m write(iulog,*) ' but there are only trac_ncnstmx = ',trac_ncnstmx,' tracers' call endrun('tracers_suite.F90:tend_tr() L457') endif nbase = mod(m-1,trac_names) + 1 if ( nbase == 1 ) then call tend_lw(ncol, q, deltat, snk) else if ( nbase == 2 ) then call tend_md(ncol, q, deltat, snk) else if ( nbase == 3 ) then call tend_hi(ncol, q, deltat, snk) else if ( nbase == 4 ) then call tend_md(ncol, q, deltat, snk) else if ( nbase == 5 ) then call tend_un(ncol, q, deltat, snk) else write(iulog,*) 'tracers_suite:tend_tr()' write(iulog,*) 'no tendency routine specified for tracer',nbase call endrun endif end subroutine tend_tr !====================================================================== subroutine tend_lw(ncol, q, deltat, snk) 1 !----------------------------------------------------------------------- ! ! Purpose: Calculate tendency (decay) of test tracer 1 (low) (null) ! ! Method: null !----------------------------------------------------------------------- implicit none !-------------------------Arguments-------------------------------------- ! integer, intent(in) :: ncol ! number of atmospheric columns real(r8), intent(in) :: q(pcols,pver) ! low mixing ratio (kg/(kg moist air)) real(r8), intent(in) :: deltat ! time step real(r8), intent(out) :: snk(pcols,pver) ! conversion rate ! (kg q /(s kg moist air)) !--------------------------Local Variables------------------------------ ! snk = 0._r8 ! write(iulog,*)'suite 1 tend_lw min/max snk',minval(snk),maxval(snk) end subroutine tend_lw !====================================================================== subroutine tend_md(ncol, q, deltat, snk) 2 !----------------------------------------------------------------------- ! ! Purpose: Calculate tendency (decay) of test tracer 2 (med) = null ! ! Method: null ! !----------------------------------------------------------------------- implicit none !-------------------------Arguments-------------------------------------- ! integer, intent(in) :: ncol ! number of atmospheric columns real(r8), intent(in) :: q(pcols,pver) ! low mixing ratio (kg/(kg moist air)) real(r8), intent(in) :: deltat ! time step real(r8), intent(out) :: snk(pcols,pver) ! conversion rate ! (kg q /(s kg moist air)) !--------------------------Local Variables------------------------------ snk = 0._r8; ! write(iulog,*)'suite 1 tend_md min/max snk',minval(snk),maxval(snk) end subroutine tend_md !====================================================================== subroutine tend_hi(ncol, q, deltat, snk) 1 !----------------------------------------------------------------------- ! ! Purpose: Calculate tendency (decay) of test tracer 3 (high) = null ! ! Method: null ! !----------------------------------------------------------------------- implicit none !-------------------------Arguments-------------------------------------- ! integer, intent(in) :: ncol ! number of atmospheric columns real(r8), intent(in) :: q(pcols,pver) ! mixing ratio (kg/(kg moist air)) real(r8), intent(in) :: deltat ! time step real(r8), intent(out) :: snk(pcols,pver) ! conversion rate ! (kg tracer /(s kg moist air)) !--------------------------Local Variables------------------------------ snk = 0._r8 ! write(iulog,*)'suite 1 tend_hi min/max snk',minval(snk),maxval(snk) end subroutine tend_hi !====================================================================== subroutine tend_un(ncol, q, deltat, snk) 1 !----------------------------------------------------------------------- ! ! Purpose: Calculate tendency (decay) of test tracer 2 (unit) = null ! ! Method: null ! !----------------------------------------------------------------------- implicit none !-------------------------Arguments-------------------------------------- ! integer, intent(in) :: ncol ! number of atmospheric columns real(r8), intent(in) :: q(pcols,pver) ! radon mixing ratio (kg/(kg dry air)) real(r8), intent(in) :: deltat ! time step real(r8), intent(out) :: snk(pcols,pver) ! conversion rate ! (kg rn /(s kg dry air)) !--------------------------Local Variables------------------------------ snk = 0._r8; ! write(iulog,*)'suite 2 tend_un min/max snk',minval(snk),maxval(snk) end subroutine tend_un !====================================================================== !====================================================================== subroutine flux_tr(m,ncol, lchnk, landfrac, flux ) 1,1 !----------------------------------------------------------------------- ! !----------------------------------------------------------------------- implicit none !--------------------------Arguments------------------------------------- integer, intent(in) :: m ! tracer number integer, intent(in) :: ncol ! number of atmospheric columns in chunk integer , intent(in) :: lchnk ! current identifier real(r8), intent(in) :: landfrac(pcols) ! landfraction real(r8), intent(out) :: flux(pcols) ! specified radon flux in kg/m^2/s !--------------------------Local Variables------------------------------ if ( m > trac_ncnstmx ) then write(iulog,*) 'tracers_suite:flux_tr()' write(iulog,*) ' asked to calculate flux for tracer number ',m write(iulog,*) ' but there are only trac_ncnstmx = ',trac_ncnstmx,' tracers' call endrun endif ! flux is null for all tracers flux = 0._r8 end subroutine flux_tr end module tracers_suite