LCOV - code coverage report
Current view: | top level - utils - hycoef.F90 (source / functions) |  |  | Hit | Total | Coverage
Test: | coverage.info |  | Lines: | 93 | 128 | 72.7 %
Date: | 2025-01-13 21:54:50 |  | Functions: | 4 | 4 | 100.0 %

              Line data    Source code
           1             : module hycoef
           2             :
           3             : use shr_kind_mod,     only: r8 => shr_kind_r8
           4             : use spmd_utils,       only: masterproc
           5             : use pmgrid,           only: plev, plevp
           6             : use cam_logfile,      only: iulog
           7             : use cam_abortutils,   only: endrun
           8             : use pio,              only: file_desc_t, var_desc_t, &
           9             :                             pio_inq_dimid, pio_inq_dimlen, pio_inq_varid, &
          10             :                             pio_double, pio_def_dim, pio_def_var, &
          11             :                             pio_put_var, pio_get_var, &
          12             :                             pio_seterrorhandling, PIO_BCAST_ERROR, PIO_NOERR
          13             :
          14             : implicit none
          15             : private
          16             : save
          17             :
          18             : !-----------------------------------------------------------------------
          19             : !
          20             : ! Purpose: Hybrid level definitions: p = a*p0 + b*ps
          21             : !          interfaces   p(k) = hyai(k)*ps0 + hybi(k)*ps
          22             : !          midpoints    p(k) = hyam(k)*ps0 + hybm(k)*ps
          23             : !
          24             : ! Note: Module data with a target attribute are targets of pointers in hist_coord_t
          25             : !       objects in the cam_history_support module.  They are associated by the calls
          26             : !       to add_hist_coord and add_vert_coord
          27             : !
          28             : !-----------------------------------------------------------------------
          29             :
          30             : real(r8), public, target :: hyai(plevp) ! ps0 component of hybrid coordinate - interfaces
          31             : real(r8), public, target :: hyam(plev)  ! ps0 component of hybrid coordinate - midpoints
          32             : real(r8), public, target :: hybi(plevp) ! ps component of hybrid coordinate - interfaces
          33             : real(r8), public, target :: hybm(plev)  ! ps component of hybrid coordinate - midpoints
          34             :
          35             : real(r8), public :: etamid(plev)      ! hybrid coordinate - midpoints
          36             :
          37             : real(r8), public :: hybd(plev)        ! difference  in b (hybi) across layers
          38             : real(r8), public :: hypi(plevp)       ! reference pressures at interfaces
          39             : real(r8), public :: hypm(plev)        ! reference pressures at midpoints
          40             : real(r8), public :: hypd(plev)        ! reference pressure layer thickness
          41             : #ifdef planet_mars
          42             : real(r8), public, protected :: ps0 = 6.0e1_r8    ! Base state surface pressure (pascals)
          43             : real(r8), public, protected :: psr = 6.0e1_r8    ! Reference surface pressure (pascals)
          44             : #else
          45             : real(r8), public, protected :: ps0 = 1.0e5_r8    ! Base state surface pressure (pascals)
          46             : real(r8), public, protected :: psr = 1.0e5_r8    ! Reference surface pressure (pascals)
          47             : #endif
          48             : real(r8), target :: alev(plev)    ! level values (hPa) for 'lev' coord
          49             : real(r8), target :: ailev(plevp)  ! interface level values for 'ilev' coord
          50             :
          51             : integer, public :: nprlev       ! number of pure pressure levels at top
          52             :
          53             : public hycoef_init
          54             :
          55             : type(var_desc_t) :: hyam_desc, hyai_desc, hybm_desc, hybi_desc, p0_desc
          56             : public init_restart_hycoef, write_restart_hycoef
          57             :
          58             : !=======================================================================
          59             : contains
          60             : !=======================================================================
          61             :
          62        1536 : subroutine hycoef_init(file, psdry)
          63             :
          64             :    use cam_history_support, only: add_hist_coord, add_vert_coord, formula_terms_t
          65             :
          66             :    !-----------------------------------------------------------------------
          67             :    !
          68             :    ! Purpose:
          69             :    ! Defines the locations of model interfaces from input data in the
          70             :    ! hybrid coordinate scheme.  Actual pressure values of model level
          71             :    ! interfaces are determined elsewhere from the fields set here.
          72             :    !
          73             :    ! Method:
          74             :    ! the following fields are set:
          75             :    ! hyai     fraction of reference pressure used for interface pressures
          76             :    ! hyam     fraction of reference pressure used for midpoint pressures
          77             :    ! hybi     fraction of surface pressure used for interface pressures
          78             :    ! hybm     fraction of surface pressure used for midpoint pressures
          79             :    ! hybd     difference of hybi's
          80             :    ! hypi     reference state interface pressures
          81             :    ! hypm     reference state midpoint pressures
          82             :    ! hypd     reference state layer thicknesses
          83             :    ! hypdln   reference state layer thicknesses (log p)
          84             :    ! hyalph   distance from interface to level (used in integrals)
          85             :    ! prsfac   log pressure extrapolation factor (used to compute psl)
          86             :    !
          87             :    ! Author: B. Boville
          88             :    !
          89             :    !-----------------------------------------------------------------------
          90             :
          91             :    ! arguments
          92             :    type(file_desc_t), intent(inout) :: file
          93             :    logical, optional, intent(in)    :: psdry  ! set true when coordinate is based
          94             :                                            ! on dry surface pressure
          95             :
          96             :    ! local variables
          97             :    integer  :: k        ! Level index
          98             :    logical  :: dry_coord
          99             :    real(r8) :: amean, bmean, atest, btest, eps
         100             :    type(formula_terms_t) :: formula_terms ! For the 'lev' and 'ilev' coords
         101             :    !-----------------------------------------------------------------------
         102             :
         103             :    ! check for dry pressure coordinate (default is moist)
         104        1536 :    dry_coord = .false.
         105        1536 :    if (present(psdry)) dry_coord = psdry
         106             :
         107             :    ! read hybrid coeficients
         108        1536 :    call hycoef_read(file)
         109             :
         110             :    ! Set layer locations
         111        1536 :    nprlev = 0
         112       41472 :    do k=1,plev
         113             :
         114             :       ! Interfaces. Set nprlev to the interface above, the first time a
         115             :       ! nonzero surface pressure contribution is found. "nprlev"
         116             :       ! identifies the lowest pure pressure interface.
         117             :
         118       41472 :       if (nprlev==0 .and. hybi(k).ne.0.0_r8) nprlev = k - 1
         119             :    end do
         120             :
         121             :    ! Set nprlev if no nonzero b's have been found. All interfaces are
         122             :    ! pure pressure. A pure pressure model requires other changes as well.
         123        1536 :    if (nprlev==0) nprlev = plev + 2
         124             :
         125             :    ! Set delta sigma part of layer thickness and reference state midpoint
         126             :    ! pressures
         127       41472 :    do k=1,plev
         128       39936 :       hybd(k) = hybi(k+1) - hybi(k)
         129       39936 :       hypm(k) = hyam(k)*ps0 + hybm(k)*psr
         130       41472 :       etamid(k) = hyam(k) + hybm(k)
         131             :    end do
         132             :
         133             :    ! Reference state interface pressures
         134       43008 :    do k=1,plevp
         135       43008 :       hypi(k) = hyai(k)*ps0 + hybi(k)*psr
         136             :    end do
         137             :
         138             :    ! Reference state layer thicknesses
         139       41472 :    do k=1,plev
         140       41472 :       hypd(k) = hypi(k+1) - hypi(k)
         141             :    end do
         142             :
         143             :    ! Test that A's and B's at full levels are arithmetic means of A's and
         144             :    ! B's at interfaces
         145        1536 :    eps    = 1.e-05_r8
         146       41472 :    do k = 1,plev
         147       39936 :       amean = ( hyai(k+1) + hyai(k) )*0.5_r8
         148       39936 :       bmean = ( hybi(k+1) + hybi(k) )*0.5_r8
         149       39936 :       if(amean == 0._r8 .and. hyam(k) == 0._r8) then
         150        1536 :          atest = 0._r8
         151             :       else
         152       38400 :          atest = abs( amean - hyam(k) )/ ( 0.5_r8*( abs(amean + hyam(k)) ) )
         153             :       endif
         154       39936 :       if(bmean == 0._r8 .and. hybm(k) == 0._r8) then
         155       10752 :          btest = 0._r8
         156             :       else
         157       29184 :          btest = abs( bmean - hybm(k) )/ ( 0.5_r8*( abs(bmean + hybm(k)) ) )
         158             :       endif
         159       39936 :       if (atest > eps) then
         160           0 :          if (masterproc) then
         161           0 :             write(iulog,9850)
         162           0 :             write(iulog,*)'k,atest,eps=',k,atest,eps
         163             :          end if
         164             :       end if
         165             :
         166       41472 :       if (btest > eps) then
         167           0 :          if (masterproc) then
         168           0 :             write(iulog,9850)
         169           0 :             write(iulog,*)'k,btest,eps=',k,btest,eps
         170             :          end if
         171             :       end if
         172             :    end do
         173             :
         174             :    ! Add the information for the 'lev' and 'ilev' mdim history coordinates
         175             :    !
         176             :    ! The hybrid coordinate used by the SE dycore is based on a dry surface
         177             :    ! pressure.  Hence it is the dry pressure rather than actual pressure
         178             :    ! that is computed by the formula_terms attribute.  This coordinate is
         179             :    ! not described by the formula
         180             :    ! atmosphere_hybrid_sigma_pressure_coordinate since the formula
         181             :    ! associated with that name uses actual pressure values.  Furthermore,
         182             :    ! the actual pressure field cannot be reconstructed from the hybrid
         183             :    ! coefficients and the surface pressure field.  Hence in the case of a
         184             :    ! dry coordinate we add neither the standard_name nor the formula_terms
         185             :    ! attributes to the lev and ilev coordinates.
         186             :
         187             :    ! 0.01 converts Pascals to millibars
         188       41472 :    alev(:plev) = 0.01_r8*ps0*(hyam(:plev) + hybm(:plev))
         189       43008 :    ailev(:plevp) = 0.01_r8*ps0*(hyai(:plevp) + hybi(:plevp))
         190             :
         191        1536 :    if (dry_coord) then
         192             :       call add_vert_coord('lev', plev,                                       &
         193             :          'hybrid level at midpoints (1000*(A+B))', 'hPa', alev,              &
         194        1536 :          positive='down')
         195             :       call add_hist_coord('hyam', plev, &
         196        1536 :          'hybrid A coefficient at layer midpoints', '1', hyam, dimname='lev')
         197             :       call add_hist_coord('hybm', plev, &
         198        1536 :          'hybrid B coefficient at layer midpoints', '1', hybm, dimname='lev')
         199             :    else
         200             :
         201           0 :       formula_terms%a_name       =  'hyam'
         202           0 :       formula_terms%a_long_name  =  'hybrid A coefficient at layer midpoints'
         203           0 :       formula_terms%a_values     => hyam
         204           0 :       formula_terms%b_name       =  'hybm'
         205           0 :       formula_terms%b_long_name  =  'hybrid B coefficient at layer midpoints'
         206           0 :       formula_terms%b_values     => hybm
         207           0 :       formula_terms%p0_name      =  'P0'
         208           0 :       formula_terms%p0_long_name = 'reference pressure'
         209           0 :       formula_terms%p0_units     =  'Pa'
         210           0 :       formula_terms%p0_value     =  ps0
         211           0 :       formula_terms%ps_name      =  'PS'
         212             :
         213             :       call add_vert_coord('lev', plev,                                       &
         214             :          'hybrid level at midpoints (1000*(A+B))', 'hPa', alev,              &
         215             :          positive='down',                                                    &
         216             :          standard_name='atmosphere_hybrid_sigma_pressure_coordinate',        &
         217           0 :          formula_terms=formula_terms)
         218             :    end if
         219             :
         220        1536 :    if (dry_coord) then
         221             :       call add_vert_coord('ilev', plevp,                                     &
         222             :          'hybrid level at interfaces (1000*(A+B))', 'hPa', ailev,            &
         223        1536 :          positive='down')
         224             :       call add_hist_coord('hyai', plevp, &
         225        1536 :          'hybrid A coefficient at layer interfaces', '1', hyai, dimname='ilev')
         226             :       call add_hist_coord('hybi', plevp, &
         227        1536 :          'hybrid B coefficient at layer interfaces', '1', hybi, dimname='ilev')
         228             :    else
         229           0 :       formula_terms%a_name       =  'hyai'
         230           0 :       formula_terms%a_long_name  =  'hybrid A coefficient at layer interfaces'
         231           0 :       formula_terms%a_values     => hyai
         232           0 :       formula_terms%b_name       =  'hybi'
         233           0 :       formula_terms%b_long_name  =  'hybrid B coefficient at layer interfaces'
         234           0 :       formula_terms%b_values     => hybi
         235           0 :       formula_terms%p0_name      =  'P0'
         236           0 :       formula_terms%p0_long_name = 'reference pressure'
         237           0 :       formula_terms%p0_units     =  'Pa'
         238           0 :       formula_terms%p0_value     =  ps0
         239           0 :       formula_terms%ps_name      =  'PS'
         240             :
         241             :       call add_vert_coord('ilev', plevp,                                     &
         242             :          'hybrid level at interfaces (1000*(A+B))', 'hPa', ailev,            &
         243             :          positive='down',                                                    &
         244             :          standard_name='atmosphere_hybrid_sigma_pressure_coordinate',        &
         245           0 :          formula_terms=formula_terms)
         246             :    end if
         247             :
         248        1536 :    if (masterproc) then
         249           2 :       write(iulog,'(a)')' Layer Locations (*1000) '
         250          54 :       do k=1,plev
         251          52 :          write(iulog,9800)k,hyai(k),hybi(k),hyai(k)+hybi(k)
         252          54 :          write(iulog,9810) hyam(k), hybm(k), hyam(k)+hybm(k)
         253             :       end do
         254             :
         255           2 :       write(iulog,9800)plevp,hyai(plevp),hybi(plevp),hyai(plevp)+hybi(plevp)
         256           2 :       write(iulog,9820)
         257          54 :       do k=1,plev
         258          52 :          write(iulog,9830) k, hypi(k)
         259          54 :          write(iulog,9840) hypm(k), hypd(k)
         260             :       end do
         261           2 :       write(iulog,9830) plevp, hypi(plevp)
         262             :     end if
         263             :
         264             : 9800 format( 1x, i3, 3p, 3(f10.4,10x) )
         265             : 9810 format( 1x, 3x, 3p, 3(10x,f10.4) )
         266             : 9820 format(1x,'reference pressures (Pa)')
         267             : 9830 format(1x,i3,f15.4)
         268             : 9840 format(1x,3x,15x,2f15.4)
         269             : 9850 format('HYCOEF: A and/or B vertical level coefficients at full',/, &
         270             :          ' levels are not the arithmetic mean of half-level values')
         271             :
         272        3072 : end subroutine hycoef_init
         273             :
         274             : !=======================================================================
         275             :
         276        1536 : subroutine init_restart_hycoef(File, vdimids)
         277             :
         278             :    type(file_desc_t), intent(inout) :: File
         279             :    integer,           intent(out)   :: vdimids(:)
         280             :
         281             :    ! PIO traps errors internally, no need to check ierr
         282             :
         283             :    integer :: ierr
         284             :
         285        1536 :    ierr = PIO_Def_Dim(File, 'lev',  plev,  vdimids(1))
         286        1536 :    ierr = PIO_Def_Dim(File, 'ilev', plevp, vdimids(2))
         287             :
         288        1536 :    ierr = pio_def_var(File, 'hyai', pio_double, vdimids(2:2), hyai_desc)
         289        1536 :    ierr = pio_def_var(File, 'hyam', pio_double, vdimids(1:1), hyam_desc)
         290        1536 :    ierr = pio_def_var(File, 'hybi', pio_double, vdimids(2:2), hybi_desc)
         291        1536 :    ierr = pio_def_var(File, 'hybm', pio_double, vdimids(1:1), hybm_desc)
         292             :
         293        1536 :    ierr = pio_def_var(File, 'P0', pio_double, p0_desc)
         294             :
         295        1536 : end subroutine init_restart_hycoef
         296             :
         297             : !=======================================================================
         298             :
         299        1536 : subroutine write_restart_hycoef(file)
         300             :
         301             :    type(file_desc_t), intent(inout) :: File
         302             :
         303             :    ! PIO traps errors internally, no need to check ierr
         304             :
         305             :    integer :: ierr
         306             :
         307        1536 :    ierr = pio_put_var(File, hyai_desc, hyai)
         308        1536 :    ierr = pio_put_var(File, hyam_desc, hyam)
         309        1536 :    ierr = pio_put_var(File, hybi_desc, hybi)
         310        1536 :    ierr = pio_put_var(File, hybm_desc, hybm)
         311             :
         312        1536 :    ierr = pio_put_var(File, p0_desc,   ps0)
         313             :
         314        1536 : end subroutine write_restart_hycoef
         315             :
         316             : !=======================================================================
         317             :
         318        1536 : subroutine hycoef_read(File)
         319             :
         320             :    ! This code is used both for initial and restart reading.
         321             :
         322             :    type(file_desc_t), intent(inout) :: File
         323             :
         324             :    integer :: flev, filev, lev_dimid, ierr
         325             :    integer :: pio_errtype
         326             :
         327             :    type(var_desc_t) :: p0_desc
         328             :
         329             :    character(len=*), parameter :: routine = 'hycoef_read'
         330             :    !----------------------------------------------------------------------------
         331             :
         332             :    ! PIO traps errors internally, no need to check ierr
         333             :
         334        1536 :    ierr = PIO_Inq_DimID(File, 'lev', lev_dimid)
         335        1536 :    ierr = PIO_Inq_dimlen(File, lev_dimid, flev)
         336        1536 :    if (plev /= flev) then
         337           0 :       write(iulog,*) routine//': ERROR: file lev does not match model. lev (file, model):',flev, plev
         338           0 :       call endrun(routine//': ERROR: file lev does not match model.')
         339             :    end if
         340             :
         341        1536 :    ierr = PIO_Inq_DimID(File, 'ilev', lev_dimid)
         342        1536 :    ierr = PIO_Inq_dimlen(File, lev_dimid, filev)
         343        1536 :    if (plevp /= filev) then
         344           0 :       write(iulog,*) routine//':ERROR: file ilev does not match model plevp (file, model):',filev, plevp
         345           0 :       call endrun(routine//':ERROR: file ilev does not match model.')
         346             :    end if
         347             :
         348        1536 :    ierr = pio_inq_varid(File, 'hyai', hyai_desc)
         349        1536 :    ierr = pio_inq_varid(File, 'hyam', hyam_desc)
         350        1536 :    ierr = pio_inq_varid(File, 'hybi', hybi_desc)
         351        1536 :    ierr = pio_inq_varid(File, 'hybm', hybm_desc)
         352             :
         353        1536 :    ierr = pio_get_var(File, hyai_desc, hyai)
         354        1536 :    ierr = pio_get_var(File, hybi_desc, hybi)
         355        1536 :    ierr = pio_get_var(File, hyam_desc, hyam)
         356        1536 :    ierr = pio_get_var(File, hybm_desc, hybm)
         357             :
         358        1536 :    if (masterproc) then
         359           2 :       write(iulog,*) routine//': read hyai, hybi, hyam, hybm'
         360             :    end if
         361             :
         362             :    ! Check whether file contains value for P0.  If it does then use it
         363             :
         364             :    ! Set PIO to return error codes.
         365        1536 :    call pio_seterrorhandling(file, PIO_BCAST_ERROR, pio_errtype)
         366             :
         367        1536 :    ierr = pio_inq_varid(file, 'P0', p0_desc)
         368        1536 :    if (ierr == PIO_NOERR) then
         369        1536 :       ierr = pio_get_var(file, p0_desc, ps0)
         370        1536 :       if (ierr /= PIO_NOERR) then
         371           0 :          call endrun(routine//': reading P0.')
         372             :       end if
         373        1536 :       psr = ps0
         374             :
         375        1536 :       if (masterproc) then
         376           2 :          write(iulog,*) routine//': read P0 value: ', ps0
         377             :       end if
         378             :
         379             :    end if
         380             :
         381             :    ! Put the error handling back the way it was
         382        1536 :    call pio_seterrorhandling(file, pio_errtype)
         383             :
         384             : #if ( defined OFFLINE_DYN )
         385             :    ! make sure top interface is non zero for fv dycore
         386             :    if (hyai(1) .eq. 0._r8) then
         387             :       if (hybm(1) .ne. 0.0_r8) then
         388             :          hyai(1) = hybm(1)*1.e-2_r8
         389             :       else if (hyam(1) .ne. 0.0_r8) then
         390             :          hyai(1) = hyam(1)*1.e-2_r8
         391             :       else
         392             :          call endrun('Not able to set hyai(1) to non-zero.')
         393             :       end if
         394             :    end if
         395             : #endif
         396             :
         397        1536 : end subroutine hycoef_read
         398             :
         399             : !=======================================================================
         400             :
         401             : end module hycoef

Generated by: LCOV version 1.14