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

Generated by: LCOV version 1.14