LCOV - code coverage report
Current view: | top level - physics/cam - phys_grid_ctem.F90 (source / functions) |  |  | Hit | Total | Coverage
Test: | coverage.info |  | Lines: | 65 | 178 | 36.5 %
Date: | 2025-01-13 21:54:50 |  | Functions: | 6 | 7 | 85.7 %

              Line data    Source code
           1             : !----------------------------------------------------------------------------------
           2             : ! circulation diagnostics -- terms of the Transformed Eulerian Mean (TEM) equation
           3             : !
           4             : !----------------------------------------------------------------------------------
           5             : module phys_grid_ctem
           6             :   use shr_kind_mod,  only: r8 => shr_kind_r8
           7             :   use ppgrid,        only: begchunk, endchunk, pcols, pver
           8             :   use physics_types, only: physics_state
           9             :   use cam_history,   only: addfld, outfld
          10             :   use zonal_mean_mod,only: ZonalAverage_t, ZonalMean_t
          11             :   use physconst,     only: pi
          12             :   use cam_logfile,   only: iulog
          13             :   use cam_abortutils,only: endrun, handle_allocate_error
          14             :   use namelist_utils,only: find_group_name
          15             :   use spmd_utils,    only: masterproc, mpi_integer, masterprocid, mpicom
          16             :   use time_manager,  only: get_step_size, get_nstep
          17             :
          18             :   use shr_const_mod, only: rgas => shr_const_rgas ! J/K/kmole
          19             :   use shr_const_mod, only: grav => shr_const_g ! m/s2
          20             :   use air_composition, only: mbarv ! g/mole
          21             :   use string_utils,  only: int2str
          22             :
          23             :   implicit none
          24             :
          25             :   private
          26             :   public :: phys_grid_ctem_readnl
          27             :   public :: phys_grid_ctem_reg
          28             :   public :: phys_grid_ctem_init
          29             :   public :: phys_grid_ctem_diags
          30             :   public :: phys_grid_ctem_final
          31             :
          32             :   type(ZonalMean_t) :: ZMobj
          33             :   type(ZonalAverage_t) :: ZAobj
          34             :
          35             :   integer :: nzalat = -huge(1)
          36             :   integer :: nzmbas = -huge(1)
          37             :
          38             :   integer :: ntimesteps = -huge(1) ! number of time steps bewteen TEM calculations
          39             :
          40             :   logical :: do_tem_diags = .false.
          41             :
          42             : contains
          43             :
          44             :   !------------------------------------------------------------------------------
          45             :   !------------------------------------------------------------------------------
          46        1536 :   subroutine phys_grid_ctem_readnl(nlfile)
          47             :     character(len=*), intent(in) :: nlfile
          48             :     integer :: ierr, unitn
          49             :
          50             :     character(len=*), parameter :: prefix = 'phys_grid_ctem_readnl: '
          51             :     real(r8) :: dtime
          52             :
          53             :     integer :: phys_grid_ctem_zm_nbas
          54             :     integer :: phys_grid_ctem_za_nlat
          55             :     integer :: phys_grid_ctem_nfreq
          56             :
          57             :     namelist /phys_grid_ctem_opts/ phys_grid_ctem_zm_nbas, phys_grid_ctem_za_nlat, phys_grid_ctem_nfreq
          58             :
          59        1536 :     phys_grid_ctem_zm_nbas = 0
          60        1536 :     phys_grid_ctem_za_nlat = 0
          61        1536 :     phys_grid_ctem_nfreq = 0
          62             :
          63             :     ! Read in namelist values
          64             :     !------------------------
          65        1536 :     if(masterproc) then
          66           2 :        open(newunit=unitn, file=trim(nlfile), status='old')
          67           2 :        call find_group_name(unitn, 'phys_grid_ctem_opts', status=ierr)
          68           2 :        if(ierr == 0) then
          69           0 :           read(unitn,phys_grid_ctem_opts,iostat=ierr)
          70           0 :           if(ierr /= 0) then
          71           0 :              call endrun(prefix//'ERROR reading namelist')
          72             :           end if
          73             :        end if
          74           2 :        close(unitn)
          75             :     end if
          76             :
          77        1536 :     call MPI_bcast(phys_grid_ctem_zm_nbas, 1, mpi_integer, masterprocid, mpicom, ierr)
          78        1536 :     if (ierr /= 0) call endrun(prefix//'FATAL: mpi_bcast: phys_grid_ctem_zm_nbas')
          79        1536 :     call MPI_bcast(phys_grid_ctem_za_nlat, 1, mpi_integer, masterprocid, mpicom, ierr)
          80        1536 :     if (ierr /= 0) call endrun(prefix//'FATAL: mpi_bcast: phys_grid_ctem_za_nlat')
          81        1536 :     call MPI_bcast(phys_grid_ctem_nfreq,   1, mpi_integer, masterprocid, mpicom, ierr)
          82        1536 :     if (ierr /= 0) call endrun(prefix//'FATAL: mpi_bcast: phys_grid_ctem_nfreq')
          83             :
          84        1536 :     do_tem_diags = .false.
          85        1536 :     if (phys_grid_ctem_nfreq/=0) then
          86           0 :        if (.not.(phys_grid_ctem_zm_nbas>0 .and. phys_grid_ctem_za_nlat>0)) then
          87             :           call endrun(prefix//'inconsistent phys_grid_ctem namelist settings -- phys_grid_ctem_zm_nbas=' &
          88           0 :                       //int2str(phys_grid_ctem_zm_nbas)//', phys_grid_ctem_za_nlat='//int2str(phys_grid_ctem_za_nlat))
          89             :        end if
          90           0 :        if (phys_grid_ctem_nfreq>0) then
          91           0 :           ntimesteps = phys_grid_ctem_nfreq
          92             :        else
          93           0 :           dtime = get_step_size()
          94           0 :           ntimesteps = nint( -phys_grid_ctem_nfreq*3600._r8/dtime )
          95             :        end if
          96           0 :        if (ntimesteps<1) then
          97             :           call endrun(prefix//'invalid ntimesteps -- phys_grid_ctem_nfreq needs to be a larger negative value ' &
          98           0 :                             //'or the model time step needs to be shorter')
          99             :        end if
         100           0 :        do_tem_diags = .true.
         101             :     end if
         102             :
         103        1536 :     if (masterproc) then
         104           2 :        if (do_tem_diags) then
         105           0 :           write(iulog,*) 'TEM diagnostics will be calculated every ',ntimesteps,' time steps'
         106           0 :           write(iulog,*) ' phys_grid_ctem_zm_nbas = ', phys_grid_ctem_zm_nbas
         107           0 :           write(iulog,*) ' phys_grid_ctem_za_nlat = ', phys_grid_ctem_za_nlat
         108           0 :           write(iulog,*) ' phys_grid_ctem_nfreq = ', phys_grid_ctem_nfreq
         109             :        else
         110           2 :           write(iulog,*) 'TEM diagnostics will not be performed'
         111             :        end if
         112             :     endif
         113             :
         114        1536 :     if (do_tem_diags) then
         115           0 :        nzalat = phys_grid_ctem_za_nlat
         116           0 :        nzmbas = phys_grid_ctem_zm_nbas
         117             :     end if
         118             :
         119        1536 :   end subroutine phys_grid_ctem_readnl
         120             :
         121             :   !-----------------------------------------------------------------------------
         122             :   !-----------------------------------------------------------------------------
         123        1536 :   subroutine phys_grid_ctem_reg
         124             :
         125             :     use cam_grid_support, only: horiz_coord_t, horiz_coord_create, iMap, cam_grid_register
         126             :
         127             :     type(horiz_coord_t), pointer :: zalon_coord
         128             :     type(horiz_coord_t), pointer :: zalat_coord
         129        1536 :     integer(iMap),       pointer :: grid_map(:,:)
         130             :
         131        3072 :     real(r8) :: zalats(nzalat)
         132        3072 :     real(r8) :: area(nzalat)
         133             :     real(r8) :: zalons(1)
         134             :     real(r8) :: dlatrad, dlatdeg, lat1, lat2
         135             :     real(r8) :: total_area
         136             :     real(r8) :: total_wght
         137             :     integer :: j, astat
         138             :
         139             :     real(r8), parameter :: latdeg0 = -90._r8
         140             :     real(r8), parameter :: latrad0 = -pi*0.5_r8
         141             :     real(r8), parameter :: fourpi = pi*4._r8
         142             :
         143             :     integer, parameter :: ctem_zavg_phys_decomp = 333 ! Must be unique within CAM
         144             :
         145        1536 :     if (.not.do_tem_diags) return
         146             :
         147           0 :     nullify(zalat_coord)
         148           0 :     nullify(zalon_coord)
         149           0 :     nullify(grid_map)
         150             :
         151           0 :     zalons(1) = 0._r8
         152             :
         153           0 :     dlatrad = pi/real(nzalat,kind=r8)
         154           0 :     dlatdeg = 180._r8/real(nzalat,kind=r8)
         155           0 :     total_area = 0._r8
         156           0 :     total_wght = 0._r8
         157             :
         158             :     ! calculate latitudes and areas of zonal average grid boxes
         159           0 :     do j = 1,nzalat
         160           0 :        zalats(j) = latdeg0 + (real(j,kind=r8)-0.5_r8)*dlatdeg
         161           0 :        lat1 = latrad0 + real(j-1,kind=r8)*dlatrad
         162           0 :        lat2 = latrad0 + real(j  ,kind=r8)*dlatrad
         163           0 :        area(j) = 2._r8*pi*(sin(lat2)-sin(lat1))
         164           0 :        total_area = total_area + area(j)
         165           0 :        total_wght = total_wght + 0.5_r8*(sin(lat2)-sin(lat1))
         166             :     end do
         167             :
         168             :     ! sanity check
         169           0 :     if ( abs(1._r8-total_wght)>1.e-12_r8 .or. abs(fourpi-total_area)>1.e-12_r8 ) then
         170           0 :        call endrun('phys_grid_ctem_reg: problem with area/wght calc')
         171             :     end if
         172             :
         173             :     ! initialize zonal-average and zonal-mean utility objects
         174           0 :     call ZAobj%init(zalats,area,nzalat,GEN_GAUSSLATS=.false.)
         175           0 :     call ZMobj%init(nzmbas)
         176             :
         177             :     ! Zonal average grid for history fields
         178             :
         179           0 :     zalat_coord => horiz_coord_create('zalat', '', nzalat, 'latitude', 'degrees_north', 1, nzalat, zalats)
         180           0 :     zalon_coord => horiz_coord_create('zalon', '', 1, 'longitude', 'degrees_east', 1, 1, zalons)
         181             :
         182             :     ! grid decomposition map
         183           0 :     allocate(grid_map(4,nzalat), stat=astat)
         184           0 :     call handle_allocate_error(astat, 'phys_grid_ctem_reg', 'grid_map')
         185             :
         186           0 :     do j = 1,nzalat
         187           0 :        grid_map(1,j) = 1
         188           0 :        grid_map(2,j) = j
         189           0 :        if (masterproc) then
         190           0 :           grid_map(3,j) = 1
         191           0 :           grid_map(4,j) = j
         192             :        else
         193           0 :           grid_map(3,j) = 0
         194           0 :           grid_map(4,j) = 0
         195             :        end if
         196             :     end do
         197             :
         198             :     ! register the zonal average grid
         199             :     call cam_grid_register('ctem_zavg_phys', ctem_zavg_phys_decomp, zalat_coord, zalon_coord, grid_map, &
         200           0 :                            unstruct=.false., zonal_grid=.true.)
         201             :
         202        1536 :   end subroutine phys_grid_ctem_reg
         203             :
         204             :   !-----------------------------------------------------------------------------
         205             :   !-----------------------------------------------------------------------------
         206        1536 :   subroutine phys_grid_ctem_init
         207             :
         208        1536 :     if (.not.do_tem_diags) return
         209             :
         210           0 :     call addfld ('Uzm',  (/'lev'/), 'A','m s-1',  'Zonal-Mean zonal wind', gridname='ctem_zavg_phys' )
         211           0 :     call addfld ('Vzm',  (/'lev'/), 'A','m s-1',  'Zonal-Mean meridional wind', gridname='ctem_zavg_phys' )
         212           0 :     call addfld ('Wzm',  (/'lev'/), 'A','m s-1',  'Zonal-Mean vertical wind', gridname='ctem_zavg_phys' )
         213           0 :     call addfld ('THzm', (/'lev'/), 'A','K',      'Zonal-Mean potential temp', gridname='ctem_zavg_phys' )
         214           0 :     call addfld ('VTHzm',(/'lev'/), 'A','K m s-1','Meridional Heat Flux:', gridname='ctem_zavg_phys')
         215           0 :     call addfld ('WTHzm',(/'lev'/), 'A','K m s-1','Vertical Heat Flux:', gridname='ctem_zavg_phys')
         216           0 :     call addfld ('UVzm', (/'lev'/), 'A','m2 s-2', 'Meridional Flux of Zonal Momentum', gridname='ctem_zavg_phys')
         217           0 :     call addfld ('UWzm', (/'lev'/), 'A','m2 s-2', 'Vertical Flux of Zonal Momentum', gridname='ctem_zavg_phys')
         218           0 :     call addfld ('THphys',(/'lev'/), 'A', 'K',    'Potential temp', gridname='physgrid' )
         219             :
         220        1536 :   end subroutine phys_grid_ctem_init
         221             :
         222             :   !-----------------------------------------------------------------------------
         223             :   !-----------------------------------------------------------------------------
         224      370944 :   subroutine phys_grid_ctem_diags(phys_state)
         225             :     type(physics_state), intent(in) :: phys_state(begchunk:endchunk)
         226             :
         227             :     character(len=*), parameter :: prefix = 'phys_grid_ctem_diags: '
         228             :
         229      741888 :     real(r8) :: u(pcols,pver,begchunk:endchunk)
         230      741888 :     real(r8) :: v(pcols,pver,begchunk:endchunk)
         231      741888 :     real(r8) :: w(pcols,pver,begchunk:endchunk)
         232             :
         233      741888 :     real(r8) :: uzm(pcols,pver,begchunk:endchunk)
         234      741888 :     real(r8) :: vzm(pcols,pver,begchunk:endchunk)
         235      741888 :     real(r8) :: wzm(pcols,pver,begchunk:endchunk)
         236             :
         237      741888 :     real(r8) :: ud(pcols,pver,begchunk:endchunk)
         238      741888 :     real(r8) :: vd(pcols,pver,begchunk:endchunk)
         239      741888 :     real(r8) :: wd(pcols,pver,begchunk:endchunk)
         240      741888 :     real(r8) :: thd(pcols,pver,begchunk:endchunk)
         241             :
         242      741888 :     real(r8) :: uvp(pcols,pver,begchunk:endchunk)
         243      741888 :     real(r8) :: uwp(pcols,pver,begchunk:endchunk)
         244      741888 :     real(r8) :: vthp(pcols,pver,begchunk:endchunk)
         245      741888 :     real(r8) :: wthp(pcols,pver,begchunk:endchunk)
         246             :
         247             :     integer  :: lchnk, ncol, j, k
         248             :
         249             :     ! potential temperature
         250      741888 :     real(r8) :: theta(pcols,pver,begchunk:endchunk)
         251      741888 :     real(r8) :: thzm(pcols,pver,begchunk:endchunk)
         252             :
         253      741888 :     real(r8) :: uvza(nzalat,pver)
         254      741888 :     real(r8) :: uwza(nzalat,pver)
         255      741888 :     real(r8) :: vthza(nzalat,pver)
         256      741888 :     real(r8) :: wthza(nzalat,pver)
         257             :
         258      741888 :     real(r8) :: uza(nzalat,pver)
         259      741888 :     real(r8) :: vza(nzalat,pver)
         260      741888 :     real(r8) :: wza(nzalat,pver)
         261      370944 :     real(r8) :: thza(nzalat,pver)
         262             :
         263             :     real(r8) :: sheight(pcols,pver) ! pressure scale height (m)
         264             :
         265      370944 :     if (.not.do_calc()) return
         266             :
         267           0 :     do lchnk = begchunk,endchunk
         268             :
         269           0 :        ncol = phys_state(lchnk)%ncol
         270             :
         271             :        ! scale height
         272           0 :        sheight(:ncol,:) = phys_state(lchnk)%t(:ncol,:) * rgas / ( mbarv(:ncol,:,lchnk) * grav ) ! meters
         273             :
         274             :        ! potential temperature
         275           0 :        theta(:ncol,:,lchnk) = phys_state(lchnk)%t(:ncol,:) * phys_state(lchnk)%exner(:ncol,:)
         276             :
         277             :        ! vertical velocity
         278           0 :        w(:ncol,:,lchnk) = -sheight(:ncol,:) *  phys_state(lchnk)%omega(:ncol,:) / phys_state(lchnk)%pmid(:ncol,:)
         279             :
         280           0 :        u(:ncol,:,lchnk) =  phys_state(lchnk)%u(:ncol,:)
         281           0 :        v(:ncol,:,lchnk) =  phys_state(lchnk)%v(:ncol,:)
         282             :
         283             :     end do
         284             :
         285             :     ! zonal means evaluated on the physics grid (3D) to be used in the deviations calculation below
         286           0 :     uzm(:,:,:) = zmean_fld(u(:,:,:))
         287           0 :     vzm(:,:,:) = zmean_fld(v(:,:,:))
         288           0 :     wzm(:,:,:) = zmean_fld(w(:,:,:))
         289           0 :     thzm(:,:,:) = zmean_fld(theta(:,:,:))
         290             :
         291             :     ! diagnostic output
         292           0 :     do lchnk = begchunk, endchunk
         293           0 :        call outfld( 'THphys', theta(:,:,lchnk), pcols, lchnk)
         294             :     end do
         295             :
         296           0 :     do lchnk = begchunk,endchunk
         297           0 :        ncol = phys_state(lchnk)%ncol
         298           0 :        do k = 1,pver
         299             :           ! zonal deviations
         300           0 :           thd(:ncol,k,lchnk) = theta(:ncol,k,lchnk) - thzm(:ncol,k,lchnk)
         301           0 :           ud(:ncol,k,lchnk) = u(:ncol,k,lchnk) - uzm(:ncol,k,lchnk)
         302           0 :           vd(:ncol,k,lchnk) = v(:ncol,k,lchnk) - vzm(:ncol,k,lchnk)
         303           0 :           wd(:ncol,k,lchnk) = w(:ncol,k,lchnk) - wzm(:ncol,k,lchnk)
         304             :           ! fluxes
         305           0 :           uvp(:ncol,k,lchnk) = ud(:ncol,k,lchnk) * vd(:ncol,k,lchnk)
         306           0 :           uwp(:ncol,k,lchnk) = ud(:ncol,k,lchnk) * wd(:ncol,k,lchnk)
         307           0 :           vthp(:ncol,k,lchnk) = vd(:ncol,k,lchnk) * thd(:ncol,k,lchnk)
         308           0 :           wthp(:ncol,k,lchnk) = wd(:ncol,k,lchnk) * thd(:ncol,k,lchnk)
         309             :        end do
         310             :     end do
         311             :
         312             :     ! evaluate and output fluxes on the zonal-average grid
         313           0 :     call ZAobj%binAvg(uvp, uvza)
         314           0 :     call ZAobj%binAvg(uwp, uwza)
         315           0 :     call ZAobj%binAvg(vthp, vthza)
         316           0 :     call ZAobj%binAvg(wthp, wthza)
         317             :
         318           0 :     if (any(abs(uvza)>1.e20_r8)) call endrun(prefix//'bad values in uvza')
         319           0 :     if (any(abs(uwza)>1.e20_r8)) call endrun(prefix//'bad values in uwza')
         320           0 :     if (any(abs(vthza)>1.e20_r8)) call endrun(prefix//'bad values in vthza')
         321           0 :     if (any(abs(wthza)>1.e20_r8)) call endrun(prefix//'bad values in wthza')
         322             :
         323           0 :     call ZAobj%binAvg(uzm, uza)
         324           0 :     call ZAobj%binAvg(vzm, vza)
         325           0 :     call ZAobj%binAvg(wzm, wza)
         326           0 :     call ZAobj%binAvg(thzm, thza)
         327             :
         328           0 :     if (any(abs(uza)>1.e20_r8)) call endrun(prefix//'bad values in uza')
         329           0 :     if (any(abs(vza)>1.e20_r8)) call endrun(prefix//'bad values in vza')
         330           0 :     if (any(abs(wza)>1.e20_r8)) call endrun(prefix//'bad values in wza')
         331           0 :     if (any(abs(thza)>1.e20_r8)) call endrun(prefix//'bad values in thza')
         332             :
         333             :     ! diagnostic output
         334           0 :     do j = 1,nzalat
         335           0 :        call outfld('Uzm',uza(j,:),1,j)
         336           0 :        call outfld('Vzm',vza(j,:),1,j)
         337           0 :        call outfld('Wzm',wza(j,:),1,j)
         338           0 :        call outfld('THzm',thza(j,:),1,j)
         339           0 :        call outfld('UVzm',uvza(j,:),1,j)
         340           0 :        call outfld('UWzm',uwza(j,:),1,j)
         341           0 :        call outfld('VTHzm',vthza(j,:),1,j)
         342           0 :        call outfld('WTHzm',wthza(j,:),1,j)
         343             :     end do
         344             :
         345             :   contains
         346             :
         347             :     !------------------------------------------------------------------------------
         348             :     ! utility function for evaluating 3D zonal mean fields
         349             :     !------------------------------------------------------------------------------
         350           0 :     function zmean_fld( fld ) result(fldzm)
         351             :
         352             :       real(r8), intent(in) :: fld(pcols,pver,begchunk:endchunk)
         353             :
         354             :       real(r8) :: fldzm(pcols,pver,begchunk:endchunk)
         355             :
         356           0 :       real(r8) :: Zonal_Bamp3d(nzmbas,pver)
         357             :
         358           0 :       call ZMobj%calc_amps(fld,Zonal_Bamp3d)
         359           0 :       call ZMobj%eval_grid(Zonal_Bamp3d,fldzm)
         360             :
         361           0 :     end function zmean_fld
         362             :
         363             :     !------------------------------------------------------------------------------
         364             :     ! utility function returns TRUE when time to update TEM diags
         365             :     !------------------------------------------------------------------------------
         366      370944 :     logical function do_calc()
         367             :
         368             :       integer :: nstep
         369      370944 :       nstep = get_nstep()
         370      370944 :       do_calc = do_tem_diags .and. mod(nstep,ntimesteps) == 0
         371             :
         372      370944 :     end function do_calc
         373             :
         374             :   end subroutine phys_grid_ctem_diags
         375             :
         376             :   !-----------------------------------------------------------------------------
         377             :   !-----------------------------------------------------------------------------
         378        1536 :   subroutine phys_grid_ctem_final
         379        1536 :     call ZAobj%final()
         380        1536 :     call ZMobj%final()
         381        1536 :   end subroutine phys_grid_ctem_final
         382             :
         383             : end module phys_grid_ctem

Generated by: LCOV version 1.14