#ifdef AIX #define USE_ESSL #endif #define USE_BDE module mo_jlong use shr_kind_mod, only : r4 => shr_kind_r4 use shr_kind_mod, only : r8 => shr_kind_r8 use cam_logfile, only : iulog use abortutils, only : endrun use wrap_nf #ifdef SPMD use mpishorthand, only : mpicom,mpiint,mpir8, mpilog, mpir4 #endif use spmd_utils, only : masterproc implicit none interface jlong module procedure jlong_photo module procedure jlong_hrates end interface private public :: jlong_init public :: jlong_timestep_init public :: jlong public :: numj save real(r8), parameter :: hc = 6.62608e-34_r8 * 2.9979e8_r8 / 1.e-9_r8 real(r8), parameter :: wc_o2_b = 242.37_r8 ! (nm) real(r8), parameter :: wc_o3_a = 310.32_r8 ! (nm) real(r8), parameter :: wc_o3_b = 1179.87_r8 ! (nm) integer :: nw ! wavelengths >200nm integer :: nt ! number of temperatures in xsection table integer :: np_xs ! number of pressure levels in xsection table integer :: numj ! number of photorates in xsqy, rsf integer :: nump ! number of altitudes in rsf integer :: numsza ! number of zen angles in rsf integer :: numalb ! number of albedos in rsf integer :: numcolo3 ! number of o3 columns in rsf real(r4), allocatable :: xsqy(:,:,:,:) real(r8), allocatable :: wc(:) real(r8), allocatable :: we(:) real(r8), allocatable :: wlintv(:) real(r8), allocatable :: etfphot(:) real(r8), allocatable :: bde_o2_b(:) real(r8), allocatable :: bde_o3_a(:) real(r8), allocatable :: bde_o3_b(:) real(r8), allocatable :: xs_o2b(:,:,:) real(r8), allocatable :: xs_o3a(:,:,:) real(r8), allocatable :: xs_o3b(:,:,:) real(r8), allocatable :: p(:) real(r8), allocatable :: del_p(:) real(r8), allocatable :: prs(:) real(r8), allocatable :: dprs(:) real(r8), allocatable :: sza(:) real(r8), allocatable :: del_sza(:) real(r8), allocatable :: alb(:) real(r8), allocatable :: del_alb(:) real(r8), allocatable :: o3rat(:) real(r8), allocatable :: del_o3rat(:) real(r8), allocatable :: colo3(:) real(r4), allocatable :: rsf_tab(:,:,:,:,:) logical :: jlong_used = .false. contains subroutine jlong_init( xs_long_file, rsf_file, lng_indexer ) use ppgrid, only : pver use time_manager, only : is_end_curr_day use mo_util, only : rebin use solar_data, only : data_nw => nbins, data_we => we, data_etf => sol_etf implicit none !------------------------------------------------------------------------------ ! ... dummy arguments !------------------------------------------------------------------------------ integer, intent(inout) :: lng_indexer(:) character(len=*), intent(in) :: xs_long_file, rsf_file !------------------------------------------------------------------------------ ! ... read Cross Section * QY NetCDF file ! find temperature index for given altitude ! derive cross*QY results, returns xsqy(nj,nz,nw) !------------------------------------------------------------------------------ call get_xsqy( xs_long_file, lng_indexer ) !------------------------------------------------------------------------------ ! ... read radiative source function NetCDF file !------------------------------------------------------------------------------ if(masterproc) write(iulog,*) 'jlong_init: before get_rsf' call get_rsf(rsf_file) if(masterproc) write(iulog,*) 'jlong_init: after get_rsf' we(:nw) = wc(:nw) - .5_r8*wlintv(:nw) we(nw+1) = wc(nw) + .5_r8*wlintv(nw) write(iulog,*) ' ' write(iulog,*) '--------------------------------------------------' call rebin( data_nw, nw, data_we, we, data_etf, etfphot ) write(iulog,*) 'jlong_init: etfphot after data rebin' write(iulog,'(1p,5g15.7)') etfphot(:) write(iulog,*) '--------------------------------------------------' write(iulog,*) ' ' jlong_used = .true. end subroutine jlong_init subroutine get_xsqy( xs_long_file, lng_indexer ) !=============================================================================! ! PURPOSE: ! ! Reads a NetCDF file that contains: ! ! cross section * QY temperature dependence, >200nm ! ! ! !=============================================================================! ! PARAMETERS: ! ! Input: ! ! filepath.... NetCDF filepath that contains the "cross sections" ! !=============================================================================! ! EDIT HISTORY: ! ! Created by Doug Kinnison, 3/14/2002 ! !=============================================================================! use ioFileMod, only : getfil use error_messages, only : alloc_err use chem_mods, only : phtcnt, pht_alias_lst, rxt_tag_lst !------------------------------------------------------------------------------ ! ... Dummy arguments !------------------------------------------------------------------------------ integer, intent(inout) :: lng_indexer(:) character(len=*) :: xs_long_file !------------------------------------------------------------------------------ ! ... Local variables !------------------------------------------------------------------------------ integer :: varid, dimid, ndx integer :: ncid integer :: iret integer :: i, k, m, n integer :: wrk_ndx(phtcnt) character(len=256) :: locfn Masterproc_only : if( masterproc ) then !------------------------------------------------------------------------------ ! ... open NetCDF File !------------------------------------------------------------------------------ call getfil(xs_long_file, locfn, 0) call wrap_open(trim(locfn), NF_NOWRITE, ncid) !------------------------------------------------------------------------------ ! ... get dimensions !------------------------------------------------------------------------------ call wrap_inq_dimid( ncid, 'numtemp', dimid ) call wrap_inq_dimlen( ncid, dimid, nt ) call wrap_inq_dimid( ncid, 'numwl', dimid ) call wrap_inq_dimlen( ncid, dimid, nw ) call wrap_inq_dimid( ncid, 'numprs', dimid ) call wrap_inq_dimlen( ncid, dimid, np_xs ) !------------------------------------------------------------------------------ ! ... check for cross section in dataset !------------------------------------------------------------------------------ do m = 1,phtcnt if( pht_alias_lst(m,2) == ' ' ) then iret = nf_inq_varid( ncid, rxt_tag_lst(m), varid ) if( iret == nf_noerr ) then lng_indexer(m) = varid end if else if( pht_alias_lst(m,2) == 'userdefined' ) then lng_indexer(m) = -1 else iret = nf_inq_varid( ncid, pht_alias_lst(m,2), varid ) if( iret == nf_noerr ) then lng_indexer(m) = varid else write(iulog,*) 'get_xsqy : ',rxt_tag_lst(m)(:len_trim(rxt_tag_lst(m))),' alias ', & pht_alias_lst(m,2)(:len_trim(pht_alias_lst(m,2))),' not in dataset' call endrun end if end if end do numj = 0 do m = 1,phtcnt if( lng_indexer(m) > 0 ) then if( any( lng_indexer(:m-1) == lng_indexer(m) ) ) then cycle end if numj = numj + 1 end if end do !------------------------------------------------------------------------------ ! ... allocate arrays !------------------------------------------------------------------------------ allocate( xsqy(numj,nw,nt,np_xs),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_xsqy', 'xsqy', numj*nt*np_xs*nw ) end if allocate( prs(np_xs),dprs(np_xs-1),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_xsqy', 'prs,dprs', np_xs ) end if allocate( xs_o2b(nw,nt,np_xs),xs_o3a(nw,nt,np_xs),xs_o3b(nw,nt,np_xs),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_xsqy', 'xs_o2b ... xs_o3b', np_xs ) end if !------------------------------------------------------------------------------ ! ... read cross sections !------------------------------------------------------------------------------ ndx = 0 do m = 1,phtcnt if( lng_indexer(m) > 0 ) then if( any( lng_indexer(:m-1) == lng_indexer(m) ) ) then cycle end if ndx = ndx + 1 call wrap_get_var_real4( ncid, lng_indexer(m), xsqy(ndx,:,:,:) ) end if end do if( ndx /= numj ) then write(iulog,*) 'get_xsqy : ndx count /= cross section count' call endrun end if call wrap_inq_varid( ncid, 'jo2_b', varid ) call wrap_get_var_realx( ncid, varid, xs_o2b ) call wrap_inq_varid( ncid, 'jo3_a', varid ) call wrap_get_var_realx( ncid, varid, xs_o3a ) call wrap_inq_varid( ncid, 'jo3_b', varid ) call wrap_get_var_realx( ncid, varid, xs_o3b ) !------------------------------------------------------------------------------ ! ... setup final lng_indexer !------------------------------------------------------------------------------ ndx = 0 wrk_ndx(:) = lng_indexer(:) do m = 1,phtcnt if( wrk_ndx(m) > 0 ) then ndx = ndx + 1 i = wrk_ndx(m) where( wrk_ndx(:) == i ) lng_indexer(:) = ndx wrk_ndx(:) = -100000 end where end if end do call wrap_inq_varid( ncid, 'pressure', varid ) call wrap_get_var_realx( ncid, varid, prs ) call wrap_close( ncid ) end if Masterproc_only #ifdef SPMD ! call mpibarrier( mpicom ) call mpibcast( numj, 1, mpiint, 0, mpicom ) call mpibcast( nt, 1, mpiint, 0, mpicom ) call mpibcast( nw, 1, mpiint, 0, mpicom ) call mpibcast( np_xs, 1, mpiint, 0, mpicom ) call mpibcast( lng_indexer, phtcnt, mpiint, 0, mpicom ) #endif if( .not. masterproc ) then !------------------------------------------------------------------------------ ! ... allocate arrays !------------------------------------------------------------------------------ allocate( xsqy(numj,nw,nt,np_xs),stat=iret ) if( iret /= nf_noerr) then write(iulog,*) 'get_xsqy : failed to allocate xsqy ; error = ',iret call endrun end if allocate( prs(np_xs),dprs(np_xs-1),stat=iret ) if( iret /= nf_noerr) then write(iulog,*) 'get_xsqy : failed to allocate prs,dprs ; error = ',iret call endrun end if allocate( xs_o2b(nw,nt,np_xs),xs_o3a(nw,nt,np_xs),xs_o3b(nw,nt,np_xs),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_xsqy', 'xs_o2b ... xs_o3b', np_xs ) end if end if #ifdef SPMD ! call mpibarrier( mpicom ) call mpibcast( prs, np_xs, mpir8, 0, mpicom ) call mpibcast( xsqy, numj*nt*np_xs*nw, mpir4, 0, mpicom ) call mpibcast( xs_o2b, nw*nt*np_xs, mpir8, 0, mpicom ) call mpibcast( xs_o3a, nw*nt*np_xs, mpir8, 0, mpicom ) call mpibcast( xs_o3b, nw*nt*np_xs, mpir8, 0, mpicom ) #endif dprs(:np_xs-1) = 1._r8/(prs(1:np_xs-1) - prs(2:np_xs)) end subroutine get_xsqy subroutine get_rsf(rsf_file) !=============================================================================! ! PURPOSE: ! ! Reads a NetCDF file that contains: ! Radiative Souce function ! !=============================================================================! ! PARAMETERS: ! ! Input: ! ! filepath.... NetCDF file that contains the RSF ! ! ! ! Output: ! ! rsf ........ Radiative Source Function (quanta cm-2 sec-1 ! ! ! ! EDIT HISTORY: ! ! Created by Doug Kinnison, 3/14/2002 ! !=============================================================================! use ioFileMod, only : getfil use error_messages, only : alloc_err !------------------------------------------------------------------------------ ! ... dummy arguments !------------------------------------------------------------------------------ character(len=*) :: rsf_file !------------------------------------------------------------------------------ ! ... local variables !------------------------------------------------------------------------------ integer :: varid, dimid integer :: ncid integer :: i, j, k, l, w integer :: iret integer :: count(5) integer :: start(5) real(r8) :: wrk character(len=256) :: locfn Masterproc_only : if( masterproc ) then !------------------------------------------------------------------------------ ! ... open NetCDF File !------------------------------------------------------------------------------ call getfil(rsf_file, locfn, 0) call wrap_open(trim(locfn), NF_NOWRITE, ncid) !------------------------------------------------------------------------------ ! ... get dimensions !------------------------------------------------------------------------------ call wrap_inq_dimid( ncid, 'numz', dimid ) call wrap_inq_dimlen( ncid, dimid, nump ) call wrap_inq_dimid( ncid, 'numsza', dimid ) call wrap_inq_dimlen( ncid, dimid, numsza ) call wrap_inq_dimid( ncid, 'numalb', dimid ) call wrap_inq_dimlen( ncid, dimid, numalb ) call wrap_inq_dimid( ncid, 'numcolo3fact', dimid ) call wrap_inq_dimlen( ncid, dimid, numcolo3 ) end if Masterproc_only #ifdef SPMD ! call mpibarrier( mpicom ) call mpibcast( nump, 1, mpiint, 0, mpicom ) call mpibcast( numsza, 1, mpiint, 0, mpicom ) call mpibcast( numalb, 1, mpiint, 0, mpicom ) call mpibcast( numcolo3, 1, mpiint, 0, mpicom ) #endif !------------------------------------------------------------------------------ ! ... allocate arrays !------------------------------------------------------------------------------ allocate( wc(nw),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'wc', nw ) end if allocate( wlintv(nw),we(nw+1),etfphot(nw),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'wlintv,etfphot', nw ) end if allocate( bde_o2_b(nw),bde_o3_a(nw),bde_o3_b(nw),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'bde', nw ) end if allocate( p(nump),del_p(nump-1),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'p,delp', nump ) end if allocate( sza(numsza),del_sza(numsza-1),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'sza,del_sza', numsza ) end if allocate( alb(numalb),del_alb(numalb-1),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'alb,del_alb', numalb ) end if allocate( o3rat(numcolo3),del_o3rat(numcolo3-1),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'o3rat,del_o3rat', numcolo3 ) end if allocate( colo3(nump),stat=iret ) if( iret /= 0 ) then call alloc_err( iret, 'get_rsf', 'colo3', nump ) end if allocate( rsf_tab(nw,nump,numsza,numcolo3,numalb),stat=iret ) if( iret /= 0 ) then write(iulog,*) 'get_rsf : dimensions = ',nw,nump,numsza,numcolo3,numalb call alloc_err( iret, 'get_rsf', 'rsf_tab', numalb*numcolo3*numsza*nump ) end if Masterproc_only2 : if( masterproc ) then !------------------------------------------------------------------------------ ! ... read variables !------------------------------------------------------------------------------ call wrap_inq_varid( ncid, 'wc', varid ) call wrap_get_var_realx( ncid, varid, wc ) call wrap_inq_varid( ncid, 'wlintv', varid ) call wrap_get_var_realx( ncid, varid, wlintv ) call wrap_inq_varid( ncid, 'pm', varid ) call wrap_get_var_realx( ncid, varid, p ) call wrap_inq_varid( ncid, 'sza', varid ) call wrap_get_var_realx( ncid, varid, sza ) call wrap_inq_varid( ncid, 'alb', varid ) call wrap_get_var_realx( ncid, varid, alb ) call wrap_inq_varid( ncid, 'colo3fact', varid ) call wrap_get_var_realx( ncid, varid, o3rat ) call wrap_inq_varid( ncid, 'colo3', varid ) call wrap_get_var_realx( ncid, varid, colo3 ) call wrap_inq_varid( ncid, 'RSF', varid ) write(iulog,*) ' ' write(iulog,*) '----------------------------------------------' write(iulog,*) 'get_rsf: numalb, numcolo3, numsza, nump = ', & numalb, numcolo3, numsza, nump write(iulog,*) 'get_rsf: size of rsf_tab = ',size(rsf_tab,dim=1),size(rsf_tab,dim=2), & size(rsf_tab,dim=3),size(rsf_tab,dim=4),size(rsf_tab,dim=5) write(iulog,*) '----------------------------------------------' write(iulog,*) ' ' call wrap_get_var_real4( ncid, varid, rsf_tab ) call wrap_close( ncid ) do w = 1,nw wrk = wlintv(w) rsf_tab(w,:,:,:,:) = wrk*rsf_tab(w,:,:,:,:) enddo end if Masterproc_only2 #ifdef SPMD call mpibcast( wc, nw, mpir8, 0, mpicom ) call mpibcast( wlintv, nw, mpir8, 0, mpicom ) call mpibcast( p, nump, mpir8, 0, mpicom ) call mpibcast( sza, numsza, mpir8, 0, mpicom ) call mpibcast( alb, numalb, mpir8, 0, mpicom ) call mpibcast( o3rat, numcolo3, mpir8, 0, mpicom ) call mpibcast( colo3, nump, mpir8, 0, mpicom ) do w = 1,nw call mpibcast( rsf_tab(w,:,:,:,:), numalb*numcolo3*numsza*nump, mpir4, 0, mpicom ) enddo #endif #ifdef USE_BDE write(iulog,*) 'Jlong using bdes' bde_o2_b(:) = max( 0._r8, hc*(wc_o2_b - wc(:))/(wc_o2_b*wc(:)) ) bde_o3_a(:) = max( 0._r8, hc*(wc_o3_a - wc(:))/(wc_o3_a*wc(:)) ) bde_o3_b(:) = max( 0._r8, hc*(wc_o3_b - wc(:))/(wc_o3_b*wc(:)) ) #else write(iulog,*) 'Jlong not using bdes' bde_o2_b(:) = hc/wc(:) bde_o3_a(:) = hc/wc(:) bde_o3_b(:) = hc/wc(:) #endif del_p(:nump-1) = 1._r8/abs(p(1:nump-1)- p(2:nump)) del_sza(:numsza-1) = 1._r8/(sza(2:numsza) - sza(1:numsza-1)) del_alb(:numalb-1) = 1._r8/(alb(2:numalb) - alb(1:numalb-1)) del_o3rat(:numcolo3-1) = 1._r8/(o3rat(2:numcolo3) - o3rat(1:numcolo3-1)) end subroutine get_rsf subroutine jlong_timestep_init !--------------------------------------------------------------- ! ... set etfphot if required !--------------------------------------------------------------- use time_manager, only : is_end_curr_day use mo_util, only : rebin use solar_data, only : data_nw => nbins, data_we => we, data_etf => sol_etf implicit none if (.not.jlong_used) return call rebin( data_nw, nw, data_we, we, data_etf, etfphot ) end subroutine jlong_timestep_init subroutine jlong_hrates( nlev, sza_in, alb_in, p_in, t_in, & mw, o2_vmr, o3_vmr, colo3_in, qrl_col, & cparg, kbot ) !============================================================================== ! Purpose: ! To calculate the thermal heating rates longward of 200nm. !============================================================================== ! Approach: ! 1) Reads the Cross Section*QY NetCDF file ! 2) Given a temperature profile, derives the appropriate XS*QY ! ! 3) Reads the Radiative Source function (RSF) NetCDF file ! Units = quanta cm-2 sec-1 ! ! 4) Indices are supplied to select a RSF that is consistent with ! the reference atmosphere in TUV (for direct comparision of J's). ! This approach will be replaced in the global model. Here colo3, zenith ! angle, and altitude will be inputed and the correct entry in the table ! will be derived. !============================================================================== use physconst, only : avogad use error_messages, only : alloc_err implicit none !------------------------------------------------------------------------------ ! ... dummy arguments !------------------------------------------------------------------------------ integer, intent (in) :: nlev ! number vertical levels integer, intent (in) :: kbot ! heating levels real(r8), intent(in) :: o2_vmr(nlev) ! o2 conc (mol/mol) real(r8), intent(in) :: o3_vmr(nlev) ! o3 conc (mol/mol) real(r8), intent(in) :: sza_in ! solar zenith angle (degrees) real(r8), intent(in) :: alb_in(nlev) ! albedo real(r8), intent(in) :: p_in(nlev) ! midpoint pressure (hPa) real(r8), intent(in) :: t_in(nlev) ! Temperature profile (K) real(r8), intent(in) :: colo3_in(nlev) ! o3 column density (molecules/cm^3) real(r8), intent(in) :: mw(nlev) ! atms molecular weight real(r8), intent(in) :: cparg(nlev) ! specific heat capacity real(r8), intent(inout) :: qrl_col(:,:) ! heating rates !---------------------------------------------------------------------- ! ... local variables !---------------------------------------------------------------------- integer :: astat integer :: k, km, m integer :: t_index ! Temperature index integer :: pndx real(r8) :: ptarget real(r8) :: delp real(r8) :: hfactor real(r8), allocatable :: rsf(:,:) ! Radiative source function real(r8), allocatable :: xswk(:,:) ! working xsection array real(r8), allocatable :: wrk(:) ! work vector !---------------------------------------------------------------------- ! ... allocate variables !---------------------------------------------------------------------- allocate( rsf(nw,kbot),stat=astat ) if( astat /= 0 ) then call alloc_err( astat, 'jlong_hrates', 'rsf', nw*nlev ) end if allocate( xswk(nw,3),wrk(nw),stat=astat ) if( astat /= 0 ) then call alloc_err( astat, 'jlong_hrates', 'xswk,wrk', 3*nw ) end if !---------------------------------------------------------------------- ! ... interpolate table rsf to model variables !---------------------------------------------------------------------- call interpolate_rsf( alb_in, sza_in, p_in, colo3_in, kbot, rsf ) !------------------------------------------------------------------------------ ! ... calculate thermal heating rates for wavelengths >200nm !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ ! LLNL LUT approach to finding temperature index... ! Calculate the temperature index into the cross section ! data which lists coss sections for temperatures from ! 150 to 350 degrees K. Make sure the index is a value ! between 1 and 201. !------------------------------------------------------------------------------ qrl_col(kbot+1:nlev,:) = 0._r8 level_loop_1 : & do k = 1,kbot !---------------------------------------------------------------------- ! ... get index into xsqy !---------------------------------------------------------------------- t_index = t_in(k) - 148.5_r8 t_index = min( 201,max( t_index,1) ) !---------------------------------------------------------------------- ! ... find pressure level !---------------------------------------------------------------------- ptarget = p_in(k) if( ptarget >= prs(1) ) then xswk(:,1) = xs_o2b(:,t_index,1) xswk(:,2) = xs_o3a(:,t_index,1) xswk(:,3) = xs_o3b(:,t_index,1) else if( ptarget <= prs(np_xs) ) then xswk(:,1) = xs_o2b(:,t_index,np_xs) xswk(:,2) = xs_o3a(:,t_index,np_xs) xswk(:,3) = xs_o3b(:,t_index,np_xs) else do km = 2,np_xs if( ptarget >= prs(km) ) then pndx = km - 1 delp = (prs(pndx) - ptarget)*dprs(pndx) exit end if end do xswk(:,1) = xs_o2b(:,t_index,pndx) & + delp*(xs_o2b(:,t_index,pndx+1) - xs_o2b(:,t_index,pndx)) xswk(:,2) = xs_o3a(:,t_index,pndx) & + delp*(xs_o3a(:,t_index,pndx+1) - xs_o3a(:,t_index,pndx)) xswk(:,3) = xs_o3b(:,t_index,pndx) & + delp*(xs_o3b(:,t_index,pndx+1) - xs_o3b(:,t_index,pndx)) end if hfactor = avogad/(cparg(k)*mw(k)) wrk(:) = xswk(:,1)*bde_o2_b(:) qrl_col(k,2) = dot_product( wrk(:),rsf(:,k) ) * o2_vmr(k) * hfactor wrk(:) = xswk(:,2)*bde_o3_a(:) qrl_col(k,3) = dot_product( wrk(:),rsf(:,k) ) * o3_vmr(k) * hfactor wrk(:) = xswk(:,3)*bde_o3_b(:) qrl_col(k,4) = dot_product( wrk(:),rsf(:,k) ) * o3_vmr(k) * hfactor end do level_loop_1 deallocate( rsf, xswk, wrk ) end subroutine jlong_hrates subroutine jlong_photo( nlev, sza_in, alb_in, p_in, t_in, & colo3_in, j_long ) !============================================================================== ! Purpose: ! To calculate the total J for selective species longward of 200nm. !============================================================================== ! Approach: ! 1) Reads the Cross Section*QY NetCDF file ! 2) Given a temperature profile, derives the appropriate XS*QY ! ! 3) Reads the Radiative Source function (RSF) NetCDF file ! Units = quanta cm-2 sec-1 ! ! 4) Indices are supplied to select a RSF that is consistent with ! the reference atmosphere in TUV (for direct comparision of J's). ! This approach will be replaced in the global model. Here colo3, zenith ! angle, and altitude will be inputed and the correct entry in the table ! will be derived. !============================================================================== use spmd_utils, only : masterproc use error_messages, only : alloc_err implicit none !------------------------------------------------------------------------------ ! ... dummy arguments !------------------------------------------------------------------------------ integer, intent (in) :: nlev ! number vertical levels real(r8), intent(in) :: sza_in ! solar zenith angle (degrees) real(r8), intent(in) :: alb_in(nlev) ! albedo real(r8), intent(in) :: p_in(nlev) ! midpoint pressure (hPa) real(r8), intent(in) :: t_in(nlev) ! Temperature profile (K) real(r8), intent(in) :: colo3_in(nlev) ! o3 column density (molecules/cm^3) real(r8), intent(out) :: j_long(:,:) ! photo rates (1/s) !---------------------------------------------------------------------- ! ... local variables !---------------------------------------------------------------------- integer :: astat integer :: k, km, m integer :: wn integer :: t_index ! Temperature index integer :: pndx real(r8) :: ptarget real(r8) :: delp real(r8) :: hfactor real(r8), allocatable :: rsf(:,:) ! Radiative source function real(r8), allocatable :: xswk(:,:) ! working xsection array !---------------------------------------------------------------------- ! ... allocate variables !---------------------------------------------------------------------- allocate( rsf(nw,nlev),stat=astat ) if( astat /= 0 ) then call alloc_err( astat, 'jlong_photo', 'rsf', nw*nlev ) end if allocate( xswk(numj,nw),stat=astat ) if( astat /= 0 ) then call alloc_err( astat, 'jlong_photo', 'xswk', numj*nw ) end if !---------------------------------------------------------------------- ! ... interpolate table rsf to model variables !---------------------------------------------------------------------- call interpolate_rsf( alb_in, sza_in, p_in, colo3_in, nlev, rsf ) !------------------------------------------------------------------------------ ! ... calculate total Jlong for wavelengths >200nm !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ ! LLNL LUT approach to finding temperature index... ! Calculate the temperature index into the cross section ! data which lists coss sections for temperatures from ! 150 to 350 degrees K. Make sure the index is a value ! between 1 and 201. !------------------------------------------------------------------------------ level_loop_1 : & do k = 1,nlev !---------------------------------------------------------------------- ! ... get index into xsqy !---------------------------------------------------------------------- t_index = t_in(k) - 148.5_r8 t_index = min( 201,max( t_index,1) ) !---------------------------------------------------------------------- ! ... find pressure level !---------------------------------------------------------------------- ptarget = p_in(k) if( ptarget >= prs(1) ) then do wn = 1,nw xswk(:,wn) = xsqy(:,wn,t_index,1) end do else if( ptarget <= prs(np_xs) ) then do wn = 1,nw xswk(:,wn) = xsqy(:,wn,t_index,np_xs) end do else do km = 2,np_xs if( ptarget >= prs(km) ) then pndx = km - 1 delp = (prs(pndx) - ptarget)*dprs(pndx) exit end if end do do wn = 1,nw xswk(:,wn) = xsqy(:,wn,t_index,pndx) & + delp*(xsqy(:,wn,t_index,pndx+1) - xsqy(:,wn,t_index,pndx)) end do end if #ifdef USE_ESSL call dgemm( 'N', 'N', numj, 1, nw, & 1._r8, xswk, numj, rsf(1,k), nw, & 0._r8, j_long(1,k), numj ) #else j_long(:,k) = matmul( xswk(:,:),rsf(:,k) ) #endif end do level_loop_1 deallocate( rsf, xswk ) end subroutine jlong_photo !---------------------------------------------------------------------- !---------------------------------------------------------------------- ! ... interpolate table rsf to model variables !---------------------------------------------------------------------- !---------------------------------------------------------------------- subroutine interpolate_rsf( alb_in, sza_in, p_in, colo3_in, kbot, rsf ) use error_messages, only : alloc_err implicit none !------------------------------------------------------------------------------ ! ... dummy arguments !------------------------------------------------------------------------------ real(r8), intent(in) :: alb_in(:) ! albedo real(r8), intent(in) :: sza_in ! solar zenith angle (degrees) integer, intent(in) :: kbot ! heating levels real(r8), intent(in) :: p_in(:) ! midpoint pressure (hPa) real(r8), intent(in) :: colo3_in(:) ! o3 column density (molecules/cm^3) real(r8), intent(out) :: rsf(:,:) !---------------------------------------------------------------------- ! ... local variables !---------------------------------------------------------------------- integer :: astat integer :: is, iv, ial integer :: isp1, ivp1, ialp1 real(r8), dimension(3) :: dels real(r8), dimension(0:1,0:1,0:1) :: wghtl, wghtu real(r8) :: psum_u real(r8), allocatable :: psum_l(:) real(r8) :: v3ratl, v3ratu integer :: pind, albind real(r8) :: wrk0, wrk1, wght1 integer :: iz, k, m integer :: izl integer :: ratindl, ratindu integer :: wn !---------------------------------------------------------------------- ! ... allocate variables !---------------------------------------------------------------------- allocate( psum_l(nw),stat=astat ) if( astat /= 0 ) then call alloc_err( astat, 'jlong_hrates', 'psum_l', nw ) end if !---------------------------------------------------------------------- ! ... find the zenith angle index ( same for all levels ) !---------------------------------------------------------------------- do is = 1,numsza if( sza(is) > sza_in ) then exit end if end do is = max( min( is,numsza ) - 1,1 ) isp1 = is + 1 dels(1) = max( 0._r8,min( 1._r8,(sza_in - sza(is)) * del_sza(is) ) ) wrk0 = 1._r8 - dels(1) izl = 2 Level_loop : & do k = kbot,1,-1 !---------------------------------------------------------------------- ! ... find albedo indicies !---------------------------------------------------------------------- do ial = 1,numalb if( alb(ial) > alb_in(k) ) then exit end if end do albind = max( min( ial,numalb ) - 1,1 ) !---------------------------------------------------------------------- ! ... find pressure level indicies !---------------------------------------------------------------------- if( p_in(k) > p(1) ) then pind = 2 wght1 = 1._r8 else if( p_in(k) <= p(nump) ) then pind = nump wght1 = 0._r8 else do iz = izl,nump if( p(iz) < p_in(k) ) then izl = iz exit end if end do pind = max( min( iz,nump ),2 ) wght1 = max( 0._r8,min( 1._r8,(p_in(k) - p(pind)) * del_p(pind-1) ) ) end if !---------------------------------------------------------------------- ! ... find "o3 ratios" !---------------------------------------------------------------------- v3ratu = colo3_in(k) / colo3(pind-1) do iv = 1,numcolo3 if( o3rat(iv) > v3ratu ) then exit end if end do ratindu = max( min( iv,numcolo3 ) - 1,1 ) if( colo3(pind) /= 0._r8 ) then v3ratl = colo3_in(k) / colo3(pind) do iv = 1,numcolo3 if( o3rat(iv) > v3ratl ) then exit end if end do ratindl = max( min( iv,numcolo3 ) - 1,1 ) else ratindl = ratindu v3ratl = o3rat(ratindu) end if !---------------------------------------------------------------------- ! ... compute the weigths !---------------------------------------------------------------------- ial = albind ialp1 = ial + 1 iv = ratindl dels(2) = max( 0._r8,min( 1._r8,(v3ratl - o3rat(iv)) * del_o3rat(iv) ) ) dels(3) = max( 0._r8,min( 1._r8,(alb_in(k) - alb(ial)) * del_alb(ial) ) ) wrk1 = (1._r8 - dels(2))*(1._r8 - dels(3)) wghtl(0,0,0) = wrk0*wrk1 wghtl(1,0,0) = dels(1)*wrk1 wrk1 = (1._r8 - dels(2))*dels(3) wghtl(0,0,1) = wrk0*wrk1 wghtl(1,0,1) = dels(1)*wrk1 wrk1 = dels(2)*(1._r8 - dels(3)) wghtl(0,1,0) = wrk0*wrk1 wghtl(1,1,0) = dels(1)*wrk1 wrk1 = dels(2)*dels(3) wghtl(0,1,1) = wrk0*wrk1 wghtl(1,1,1) = dels(1)*wrk1 iv = ratindu dels(2) = max( 0._r8,min( 1._r8,(v3ratu - o3rat(iv)) * del_o3rat(iv) ) ) wrk1 = (1._r8 - dels(2))*(1._r8 - dels(3)) wghtu(0,0,0) = wrk0*wrk1 wghtu(1,0,0) = dels(1)*wrk1 wrk1 = (1._r8 - dels(2))*dels(3) wghtu(0,0,1) = wrk0*wrk1 wghtu(1,0,1) = dels(1)*wrk1 wrk1 = dels(2)*(1._r8 - dels(3)) wghtu(0,1,0) = wrk0*wrk1 wghtu(1,1,0) = dels(1)*wrk1 wrk1 = dels(2)*dels(3) wghtu(0,1,1) = wrk0*wrk1 wghtu(1,1,1) = dels(1)*wrk1 iz = pind iv = ratindl ivp1 = iv + 1 do wn = 1,nw psum_l(wn) = wghtl(0,0,0) * rsf_tab(wn,iz,is,iv,ial) & + wghtl(0,0,1) * rsf_tab(wn,iz,is,iv,ialp1) & + wghtl(0,1,0) * rsf_tab(wn,iz,is,ivp1,ial) & + wghtl(0,1,1) * rsf_tab(wn,iz,is,ivp1,ialp1) & + wghtl(1,0,0) * rsf_tab(wn,iz,isp1,iv,ial) & + wghtl(1,0,1) * rsf_tab(wn,iz,isp1,iv,ialp1) & + wghtl(1,1,0) * rsf_tab(wn,iz,isp1,ivp1,ial) & + wghtl(1,1,1) * rsf_tab(wn,iz,isp1,ivp1,ialp1) end do iz = iz - 1 iv = ratindu ivp1 = iv + 1 do wn = 1,nw psum_u = wghtu(0,0,0) * rsf_tab(wn,iz,is,iv,ial) & + wghtu(0,0,1) * rsf_tab(wn,iz,is,iv,ialp1) & + wghtu(0,1,0) * rsf_tab(wn,iz,is,ivp1,ial) & + wghtu(0,1,1) * rsf_tab(wn,iz,is,ivp1,ialp1) & + wghtu(1,0,0) * rsf_tab(wn,iz,isp1,iv,ial) & + wghtu(1,0,1) * rsf_tab(wn,iz,isp1,iv,ialp1) & + wghtu(1,1,0) * rsf_tab(wn,iz,isp1,ivp1,ial) & + wghtu(1,1,1) * rsf_tab(wn,iz,isp1,ivp1,ialp1) rsf(wn,k) = (psum_l(wn) + wght1*(psum_u - psum_l(wn))) end do !------------------------------------------------------------------------------ ! etfphot comes in as photons/cm^2/sec/nm (rsf includes the wlintv factor -- nm) ! ... --> convert to photons/cm^2/s !------------------------------------------------------------------------------ rsf(:,k) = etfphot(:) * rsf(:,k) end do Level_loop deallocate( psum_l ) end subroutine interpolate_rsf end module mo_jlong