LCOV - code coverage report
Current view: top level - physics/cam - check_energy.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 187 341 54.8 %
Date: 2025-03-13 18:42:46 Functions: 11 12 91.7 %

          Line data    Source code
       1             : 
       2             : module check_energy
       3             : 
       4             : !---------------------------------------------------------------------------------
       5             : ! Purpose:
       6             : !
       7             : ! Module to check
       8             : !   1. vertically integrated total energy and water conservation for each
       9             : !      column within the physical parameterizations
      10             : !
      11             : !   2. global mean total energy conservation between the physics output state
      12             : !      and the input state on the next time step.
      13             : !
      14             : !   3. add a globally uniform heating term to account for any change of total energy in 2.
      15             : !
      16             : ! Author: Byron Boville  Oct 31, 2002
      17             : !
      18             : ! Modifications:
      19             : !   03.03.29  Boville  Add global energy check and fixer.
      20             : !
      21             : !---------------------------------------------------------------------------------
      22             : 
      23             :   use shr_kind_mod,    only: r8 => shr_kind_r8
      24             :   use ppgrid,          only: pcols, pver, begchunk, endchunk
      25             :   use spmd_utils,      only: masterproc
      26             : 
      27             :   use gmean_mod,       only: gmean
      28             :   use physconst,       only: gravit, rga, latvap, latice, cpair, rair
      29             :   use air_composition, only: cpairv, rairv, cp_or_cv_dycore
      30             :   use physics_types,   only: physics_state, physics_tend, physics_ptend, physics_ptend_init
      31             :   use constituents,    only: cnst_get_ind, pcnst, cnst_name, cnst_get_type_byind
      32             :   use time_manager,    only: is_first_step
      33             :   use cam_logfile,     only: iulog
      34             : 
      35             :   implicit none
      36             :   private
      37             : 
      38             : ! Public types:
      39             :   public check_tracers_data
      40             : 
      41             : ! Public methods
      42             :   public :: check_energy_readnl    ! read namelist values
      43             :   public :: check_energy_register  ! register fields in physics buffer
      44             :   public :: check_energy_get_integrals ! get energy integrals computed in check_energy_gmean
      45             :   public :: check_energy_init      ! initialization of module
      46             :   public :: check_energy_timestep_init  ! timestep initialization of energy integrals and cumulative boundary fluxes
      47             :   public :: check_energy_chng      ! check changes in integrals against cumulative boundary fluxes
      48             :   public :: check_energy_gmean     ! global means of physics input and output total energy
      49             :   public :: check_energy_fix       ! add global mean energy difference as a heating
      50             :   public :: check_tracers_init      ! initialize tracer integrals and cumulative boundary fluxes
      51             :   public :: check_tracers_chng      ! check changes in integrals against cumulative boundary fluxes
      52             : 
      53             :   public :: tot_energy_phys ! calculate and output total energy and axial angular momentum diagnostics
      54             : 
      55             : ! Private module data
      56             : 
      57             :   logical  :: print_energy_errors = .false.
      58             : 
      59             :   real(r8) :: teout_glob           ! global mean energy of output state
      60             :   real(r8) :: teinp_glob           ! global mean energy of input state
      61             :   real(r8) :: tedif_glob           ! global mean energy difference
      62             :   real(r8) :: psurf_glob           ! global mean surface pressure
      63             :   real(r8) :: ptopb_glob           ! global mean top boundary pressure
      64             :   real(r8) :: heat_glob            ! global mean heating rate
      65             : 
      66             : ! Physics buffer indices
      67             : 
      68             :   integer  :: teout_idx  = 0       ! teout index in physics buffer
      69             :   integer  :: dtcore_idx = 0       ! dtcore index in physics buffer
      70             :   integer  :: dqcore_idx = 0       ! dqcore index in physics buffer
      71             :   integer  :: ducore_idx = 0       ! ducore index in physics buffer
      72             :   integer  :: dvcore_idx = 0       ! dvcore index in physics buffer
      73             : 
      74             :   type check_tracers_data
      75             :      real(r8) :: tracer(pcols,pcnst)       ! initial vertically integrated total (kinetic + static) energy
      76             :      real(r8) :: tracer_tnd(pcols,pcnst)   ! cumulative boundary flux of total energy
      77             :      integer :: count(pcnst)               ! count of values with significant imbalances
      78             :   end type check_tracers_data
      79             : 
      80             : 
      81             : !===============================================================================
      82             : contains
      83             : !===============================================================================
      84             : 
      85        1536 : subroutine check_energy_readnl(nlfile)
      86             : 
      87             :    use namelist_utils,  only: find_group_name
      88             :    use units,           only: getunit, freeunit
      89             :    use spmd_utils,      only: mpicom, mstrid=>masterprocid, mpi_logical
      90             :    use cam_abortutils,  only: endrun
      91             : 
      92             :    character(len=*), intent(in) :: nlfile  ! filepath for file containing namelist input
      93             : 
      94             :    ! Local variables
      95             :    integer :: unitn, ierr
      96             :    character(len=*), parameter :: sub = 'check_energy_readnl'
      97             : 
      98             :    namelist /check_energy_nl/ print_energy_errors
      99             :    !-----------------------------------------------------------------------------
     100             : 
     101             :    ! Read namelist
     102        1536 :    if (masterproc) then
     103           2 :       unitn = getunit()
     104           2 :       open( unitn, file=trim(nlfile), status='old' )
     105           2 :       call find_group_name(unitn, 'check_energy_nl', status=ierr)
     106           2 :       if (ierr == 0) then
     107           2 :          read(unitn, check_energy_nl, iostat=ierr)
     108           2 :          if (ierr /= 0) then
     109           0 :             call endrun(sub//': FATAL: reading namelist')
     110             :          end if
     111             :       end if
     112           2 :       close(unitn)
     113           2 :       call freeunit(unitn)
     114             :    end if
     115             : 
     116        1536 :    call mpi_bcast(print_energy_errors, 1, mpi_logical, mstrid, mpicom, ierr)
     117        1536 :    if (ierr /= 0) call endrun(sub//": FATAL: mpi_bcast: print_energy_errors")
     118             : 
     119        1536 :    if (masterproc) then
     120           2 :       write(iulog,*) 'check_energy options:'
     121           2 :       write(iulog,*) '  print_energy_errors =', print_energy_errors
     122             :    end if
     123             : 
     124        1536 : end subroutine check_energy_readnl
     125             : 
     126             : !===============================================================================
     127             : 
     128        1536 :   subroutine check_energy_register()
     129             : !
     130             : ! Register fields in the physics buffer.
     131             : !
     132             : !-----------------------------------------------------------------------
     133             : 
     134             :     use physics_buffer, only : pbuf_add_field, dtype_r8, dyn_time_lvls
     135             :     use physics_buffer, only : pbuf_register_subcol
     136             :     use subcol_utils,   only : is_subcol_on
     137             : 
     138             : !-----------------------------------------------------------------------
     139             : 
     140             : ! Request physics buffer space for fields that persist across timesteps.
     141             : 
     142        4608 :     call pbuf_add_field('TEOUT', 'global',dtype_r8 , (/pcols,dyn_time_lvls/),      teout_idx)
     143        6144 :     call pbuf_add_field('DTCORE','global',dtype_r8,  (/pcols,pver,dyn_time_lvls/),dtcore_idx)
     144             :     ! DQCORE refers to dycore tendency of water vapor
     145        6144 :     call pbuf_add_field('DQCORE','global',dtype_r8,  (/pcols,pver,dyn_time_lvls/),dqcore_idx)
     146        6144 :     call pbuf_add_field('DUCORE','global',dtype_r8,  (/pcols,pver,dyn_time_lvls/),ducore_idx)
     147        6144 :     call pbuf_add_field('DVCORE','global',dtype_r8,  (/pcols,pver,dyn_time_lvls/),dvcore_idx)
     148        1536 :     if(is_subcol_on()) then
     149           0 :       call pbuf_register_subcol('TEOUT', 'phys_register', teout_idx)
     150           0 :       call pbuf_register_subcol('DTCORE', 'phys_register', dtcore_idx)
     151           0 :       call pbuf_register_subcol('DQCORE', 'phys_register', dqcore_idx)
     152           0 :       call pbuf_register_subcol('DUCORE', 'phys_register', ducore_idx)
     153           0 :       call pbuf_register_subcol('DVCORE', 'phys_register', dvcore_idx)
     154             :     end if
     155             : 
     156        1536 :   end subroutine check_energy_register
     157             : 
     158             : !===============================================================================
     159             : 
     160       58824 : subroutine check_energy_get_integrals( tedif_glob_out, heat_glob_out )
     161             : 
     162             : !-----------------------------------------------------------------------
     163             : ! Purpose: Return energy integrals
     164             : !-----------------------------------------------------------------------
     165             : 
     166             :      real(r8), intent(out), optional :: tedif_glob_out
     167             :      real(r8), intent(out), optional :: heat_glob_out
     168             : 
     169             : !-----------------------------------------------------------------------
     170             : 
     171       58824 :    if ( present(tedif_glob_out) ) then
     172           0 :       tedif_glob_out = tedif_glob
     173             :    endif
     174       58824 :    if ( present(heat_glob_out) ) then
     175       58824 :       heat_glob_out = heat_glob
     176             :    endif
     177             : 
     178        1536 : end subroutine check_energy_get_integrals
     179             : 
     180             : !================================================================================================
     181             : 
     182        1536 :   subroutine check_energy_init()
     183             : !
     184             : ! Initialize the energy conservation module
     185             : !
     186             : !-----------------------------------------------------------------------
     187             :     use cam_history,       only: addfld, add_default, horiz_only
     188             :     use phys_control,      only: phys_getopts
     189             : 
     190             :     implicit none
     191             : 
     192             :     logical          :: history_budget, history_waccm
     193             :     integer          :: history_budget_histfile_num ! output history file number for budget fields
     194             : 
     195             : !-----------------------------------------------------------------------
     196             : 
     197             :     call phys_getopts( history_budget_out = history_budget, &
     198             :                        history_budget_histfile_num_out = history_budget_histfile_num, &
     199        1536 :                        history_waccm_out = history_waccm )
     200             : 
     201             : ! register history variables
     202        1536 :     call addfld('TEINP',  horiz_only,  'A', 'J/m2', 'Total energy of physics input')
     203        1536 :     call addfld('TEOUT',  horiz_only,  'A', 'J/m2', 'Total energy of physics output')
     204        1536 :     call addfld('TEFIX',  horiz_only,  'A', 'J/m2', 'Total energy after fixer')
     205        1536 :     call addfld('EFIX',   horiz_only,  'A', 'W/m2', 'Effective sensible heat flux due to energy fixer')
     206        3072 :     call addfld('DTCORE', (/ 'lev' /), 'A', 'K/s' , 'T tendency due to dynamical core')
     207        3072 :     call addfld('DQCORE', (/ 'lev' /), 'A', 'kg/kg/s' , 'Water vapor tendency due to dynamical core')
     208             : 
     209        1536 :     if ( history_budget ) then
     210           0 :        call add_default ('DTCORE', history_budget_histfile_num, ' ')
     211             :     end if
     212        1536 :     if ( history_waccm ) then
     213           0 :        call add_default ('DTCORE', 1, ' ')
     214             :     end if
     215             : 
     216        1536 :   end subroutine check_energy_init
     217             : 
     218             : !===============================================================================
     219             : 
     220       65016 :   subroutine check_energy_timestep_init(state, tend, pbuf, col_type)
     221        1536 :     use cam_thermo,      only: get_hydrostatic_energy
     222             :     use physics_buffer,  only: physics_buffer_desc, pbuf_set_field
     223             :     use cam_abortutils,  only: endrun
     224             :     use dyn_tests_utils, only: vc_physics, vc_dycore, vc_height, vc_dry_pressure
     225             :     use physics_types,   only: phys_te_idx, dyn_te_idx
     226             : !-----------------------------------------------------------------------
     227             : ! Compute initial values of energy and water integrals,
     228             : ! zero cumulative tendencies
     229             : !-----------------------------------------------------------------------
     230             : !------------------------------Arguments--------------------------------
     231             : 
     232             :     type(physics_state),   intent(inout)    :: state
     233             :     type(physics_tend ),   intent(inout)    :: tend
     234             :     type(physics_buffer_desc), pointer      :: pbuf(:)
     235             :     integer, optional                       :: col_type  ! Flag inidicating whether using grid or subcolumns
     236             : !---------------------------Local storage-------------------------------
     237      130032 :     real(r8)              :: cp_or_cv(state%psetcols,pver)
     238             :     integer lchnk                                  ! chunk identifier
     239             :     integer ncol                                   ! number of atmospheric columns
     240             : !-----------------------------------------------------------------------
     241             : 
     242       65016 :     lchnk = state%lchnk
     243       65016 :     ncol  = state%ncol
     244             : 
     245             :     ! cp_or_cv needs to be allocated to a size which matches state and ptend
     246             :     ! If psetcols == pcols, cpairv is the correct size and just copy into cp_or_cv
     247             :     ! If psetcols > pcols and all cpairv match cpair, then assign the constant cpair
     248             : 
     249       65016 :     if (state%psetcols == pcols) then
     250   102855312 :        cp_or_cv(:,:) = cpairv(:,:,lchnk)
     251           0 :     else if (state%psetcols > pcols .and. all(cpairv(:,:,lchnk) == cpair)) then
     252           0 :        cp_or_cv(1:ncol,:) = cpair
     253             :     else
     254           0 :        call endrun('check_energy_timestep_init: cpairv is not allowed to vary when subcolumns are turned on')
     255             :     end if
     256             :     !
     257             :     ! CAM physics total energy
     258             :     !
     259      195048 :     call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     260      195048 :          state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     261      390096 :          state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), state%T(1:ncol,1:pver),     &
     262      130032 :          vc_physics, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),&
     263      975240 :          te = state%te_ini(1:ncol,phys_te_idx), H2O = state%tw_ini(1:ncol,phys_te_idx))
     264             :     !
     265             :     ! Dynamical core total energy
     266             :     !
     267   101027304 :     state%temp_ini(:ncol,:) = state%T(:ncol,:)
     268   101027304 :     state%z_ini(:ncol,:)    = state%zm(:ncol,:)
     269       65016 :     if (vc_dycore == vc_height) then
     270             :       !
     271             :       ! MPAS specific hydrostatic energy computation (internal energy)
     272             :       !
     273           0 :       if (state%psetcols == pcols) then
     274           0 :         cp_or_cv(:ncol,:) = cp_or_cv_dycore(:ncol,:,lchnk)
     275             :       else
     276           0 :         cp_or_cv(:ncol,:) = cpair-rair
     277             :       endif
     278             : 
     279           0 :       call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     280           0 :            state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     281           0 :            state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), state%T(1:ncol,1:pver),     &
     282           0 :            vc_dycore, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),         &
     283           0 :            z_mid = state%z_ini(1:ncol,:),                                              &
     284           0 :            te = state%te_ini(1:ncol,dyn_te_idx), H2O = state%tw_ini(1:ncol,dyn_te_idx))
     285       65016 :     else if (vc_dycore == vc_dry_pressure) then
     286             :       !
     287             :       ! SE specific hydrostatic energy (enthalpy)
     288             :       !
     289       65016 :       if (state%psetcols == pcols) then
     290   101027304 :         cp_or_cv(:ncol,:) = cp_or_cv_dycore(:ncol,:,lchnk)
     291             :       else
     292           0 :         cp_or_cv(:ncol,:) = cpair
     293             :       endif
     294      195048 :       call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     295      130032 :            state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     296      390096 :            state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), state%T(1:ncol,1:pver),     &
     297      130032 :            vc_dry_pressure, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),   &
     298      910224 :            te = state%te_ini(1:ncol,dyn_te_idx), H2O = state%tw_ini(1:ncol,dyn_te_idx))
     299             :     else
     300             :       !
     301             :       ! dycore energy is the same as physics
     302             :       !
     303           0 :       state%te_ini(1:ncol,dyn_te_idx) = state%te_ini(1:ncol,phys_te_idx)
     304           0 :       state%tw_ini(1:ncol,dyn_te_idx) = state%tw_ini(1:ncol,phys_te_idx)
     305             :     end if
     306     2236248 :     state%te_cur(:ncol,:) = state%te_ini(:ncol,:)
     307     2236248 :     state%tw_cur(:ncol,:) = state%tw_ini(:ncol,:)
     308             : 
     309             : ! zero cummulative boundary fluxes
     310     1150632 :     tend%te_tnd(:ncol) = 0._r8
     311     1150632 :     tend%tw_tnd(:ncol) = 0._r8
     312             : 
     313       65016 :     state%count = 0
     314             : 
     315             : ! initialize physics buffer
     316       65016 :     if (is_first_step()) then
     317        3096 :        call pbuf_set_field(pbuf, teout_idx, state%te_ini(:,dyn_te_idx), col_type=col_type)
     318             :     end if
     319             : 
     320       65016 :   end subroutine check_energy_timestep_init
     321             : 
     322             : !===============================================================================
     323             : 
     324     1012392 :   subroutine check_energy_chng(state, tend, name, nstep, ztodt,        &
     325     1012392 :        flx_vap, flx_cnd, flx_ice, flx_sen)
     326       65016 :     use cam_thermo,      only: get_hydrostatic_energy
     327             :     use dyn_tests_utils, only: vc_physics, vc_dycore, vc_height, vc_dry_pressure
     328             :     use cam_abortutils,  only: endrun
     329             :     use physics_types,   only: phys_te_idx, dyn_te_idx
     330             : !-----------------------------------------------------------------------
     331             : ! Check that the energy and water change matches the boundary fluxes
     332             : !-----------------------------------------------------------------------
     333             : !------------------------------Arguments--------------------------------
     334             : 
     335             :     type(physics_state)    , intent(inout) :: state
     336             :     type(physics_tend )    , intent(inout) :: tend
     337             :     character*(*),intent(in) :: name               ! parameterization name for fluxes
     338             :     integer , intent(in   ) :: nstep               ! current timestep number
     339             :     real(r8), intent(in   ) :: ztodt               ! 2 delta t (model time increment)
     340             :     real(r8), intent(in   ) :: flx_vap(:)          ! (pcols) - boundary flux of vapor         (kg/m2/s)
     341             :     real(r8), intent(in   ) :: flx_cnd(:)          ! (pcols) -boundary flux of liquid+ice    (m/s) (precip?)
     342             :     real(r8), intent(in   ) :: flx_ice(:)          ! (pcols) -boundary flux of ice           (m/s) (snow?)
     343             :     real(r8), intent(in   ) :: flx_sen(:)          ! (pcols) -boundary flux of sensible heat (w/m2)
     344             : 
     345             : !******************** BAB ******************************************************
     346             : !******* Note that the precip and ice fluxes are in precip units (m/s). ********
     347             : !******* I would prefer to have kg/m2/s.                                ********
     348             : !******* I would also prefer liquid (not total) and ice fluxes          ********
     349             : !*******************************************************************************
     350             : 
     351             : !---------------------------Local storage-------------------------------
     352             : 
     353     2024784 :     real(r8) :: te_xpd(state%ncol)                 ! expected value (f0 + dt*boundary_flux)
     354     2024784 :     real(r8) :: te_dif(state%ncol)                 ! energy of input state - original energy
     355     2024784 :     real(r8) :: te_tnd(state%ncol)                 ! tendency from last process
     356     2024784 :     real(r8) :: te_rer(state%ncol)                 ! relative error in energy column
     357             : 
     358     2024784 :     real(r8) :: tw_xpd(state%ncol)                 ! expected value (w0 + dt*boundary_flux)
     359     2024784 :     real(r8) :: tw_dif(state%ncol)                 ! tw_inp - original water
     360     2024784 :     real(r8) :: tw_tnd(state%ncol)                 ! tendency from last process
     361     2024784 :     real(r8) :: tw_rer(state%ncol)                 ! relative error in water column
     362             : 
     363     2024784 :     real(r8) :: te(state%ncol)                     ! vertical integral of total energy
     364     2024784 :     real(r8) :: tw(state%ncol)                     ! vertical integral of total water
     365     2024784 :     real(r8) :: cp_or_cv(state%psetcols,pver)      ! cp or cv depending on vcoord
     366     2024784 :     real(r8) :: scaling(state%psetcols,pver)       ! scaling for conversion of temperature increment
     367     2024784 :     real(r8) :: temp(state%ncol,pver)              ! temperature
     368             : 
     369     2024784 :     real(r8) :: se(state%ncol)                     ! enthalpy or internal energy (J/m2)
     370     2024784 :     real(r8) :: po(state%ncol)                     ! surface potential or potential energy (J/m2)
     371     2024784 :     real(r8) :: ke(state%ncol)                     ! kinetic energy    (J/m2)
     372     2024784 :     real(r8) :: wv(state%ncol)                     ! column integrated vapor       (kg/m2)
     373     2024784 :     real(r8) :: liq(state%ncol)                    ! column integrated liquid      (kg/m2)
     374     2024784 :     real(r8) :: ice(state%ncol)                    ! column integrated ice         (kg/m2)
     375             : 
     376             :     integer lchnk                                  ! chunk identifier
     377             :     integer ncol                                   ! number of atmospheric columns
     378             :     integer  i                                     ! column index
     379             : !-----------------------------------------------------------------------
     380             : 
     381     1012392 :     lchnk = state%lchnk
     382     1012392 :     ncol  = state%ncol
     383             : 
     384             :     ! If psetcols == pcols, cpairv is the correct size and just copy into cp_or_cv
     385             :     ! If psetcols > pcols and all cpairv match cpair, then assign the constant cpair
     386             : 
     387     1012392 :     if (state%psetcols == pcols) then
     388  1601604144 :        cp_or_cv(:,:) = cpairv(:,:,lchnk)
     389           0 :     else if (state%psetcols > pcols .and. all(cpairv(:,:,:) == cpair)) then
     390           0 :        cp_or_cv(:,:) = cpair
     391             :     else
     392           0 :        call endrun('check_energy_chng: cpairv is not allowed to vary when subcolumns are turned on')
     393             :     end if
     394             : 
     395     3037176 :     call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     396     3037176 :          state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     397     6074352 :          state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), state%T(1:ncol,1:pver),     &
     398     2024784 :          vc_physics, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),        &
     399             :          te = te(1:ncol), H2O = tw(1:ncol), se=se(1:ncol),po=po(1:ncol),             &
     400    15185880 :          ke=ke(1:ncol),wv=wv(1:ncol),liq=liq(1:ncol),ice=ice(1:ncol))
     401             :     ! compute expected values and tendencies
     402    16904592 :     do i = 1, ncol
     403             :        ! change in static energy and total water
     404    15892200 :        te_dif(i) = te(i) - state%te_cur(i,phys_te_idx)
     405    15892200 :        tw_dif(i) = tw(i) - state%tw_cur(i,phys_te_idx)
     406             : 
     407             :        ! expected tendencies from boundary fluxes for last process
     408    15892200 :        te_tnd(i) = flx_vap(i)*(latvap+latice) - (flx_cnd(i) - flx_ice(i))*1000._r8*latice + flx_sen(i)
     409    15892200 :        tw_tnd(i) = flx_vap(i) - flx_cnd(i) *1000._r8
     410             : 
     411             :        ! cummulative tendencies from boundary fluxes
     412    15892200 :        tend%te_tnd(i) = tend%te_tnd(i) + te_tnd(i)
     413    15892200 :        tend%tw_tnd(i) = tend%tw_tnd(i) + tw_tnd(i)
     414             : 
     415             :        ! expected new values from previous state plus boundary fluxes
     416    15892200 :        te_xpd(i) = state%te_cur(i,phys_te_idx) + te_tnd(i)*ztodt
     417    15892200 :        tw_xpd(i) = state%tw_cur(i,phys_te_idx) + tw_tnd(i)*ztodt
     418             : 
     419             :        ! relative error, expected value - input state / previous state
     420    16904592 :        te_rer(i) = (te_xpd(i) - te(i)) / state%te_cur(i,phys_te_idx)
     421             :     end do
     422             : 
     423             :     ! relative error for total water (allow for dry atmosphere)
     424    16904592 :     tw_rer = 0._r8
     425    16904592 :     where (state%tw_cur(:ncol,phys_te_idx) > 0._r8)
     426     1012392 :        tw_rer(:ncol) = (tw_xpd(:ncol) - tw(:ncol)) / state%tw_cur(:ncol,1)
     427             :     end where
     428             : 
     429             :     ! error checking
     430     1012392 :     if (print_energy_errors) then
     431           0 :        if (any(abs(te_rer(1:ncol)) > 1.E-14_r8 .or. abs(tw_rer(1:ncol)) > 1.E-10_r8)) then
     432           0 :           do i = 1, ncol
     433             :              ! the relative error threshold for the water budget has been reduced to 1.e-10
     434             :              ! to avoid messages generated by QNEG3 calls
     435             :              ! PJR- change to identify if error in energy or water
     436           0 :              if (abs(te_rer(i)) > 1.E-14_r8 ) then
     437           0 :                 state%count = state%count + 1
     438           0 :                 write(iulog,*) "significant energy conservation error after ", name,        &
     439           0 :                       " count", state%count, " nstep", nstep, "chunk", lchnk, "col", i
     440           0 :                 write(iulog,*) te(i),te_xpd(i),te_dif(i),tend%te_tnd(i)*ztodt,  &
     441           0 :                       te_tnd(i)*ztodt,te_rer(i)
     442             :              endif
     443           0 :              if ( abs(tw_rer(i)) > 1.E-10_r8) then
     444           0 :                 state%count = state%count + 1
     445           0 :                 write(iulog,*) "significant water conservation error after ", name,        &
     446           0 :                       " count", state%count, " nstep", nstep, "chunk", lchnk, "col", i
     447           0 :                 write(iulog,*) tw(i),tw_xpd(i),tw_dif(i),tend%tw_tnd(i)*ztodt,  &
     448           0 :                       tw_tnd(i)*ztodt,tw_rer(i)
     449             :              end if
     450             :           end do
     451             :        end if
     452             :     end if
     453             : 
     454             :     ! copy new value to state
     455             : 
     456    16904592 :     do i = 1, ncol
     457    15892200 :       state%te_cur(i,phys_te_idx) = te(i)
     458    16904592 :       state%tw_cur(i,phys_te_idx) = tw(i)
     459             :     end do
     460             : 
     461             :     !
     462             :     ! Dynamical core total energy
     463             :     !
     464     1012392 :     if (vc_dycore == vc_height) then
     465             :       !
     466             :       ! compute cv if vertical coordinate is height: cv = cp - R
     467             :       !
     468             :       ! Note: cp_or_cv set above for pressure coordinate
     469           0 :       if (state%psetcols == pcols) then
     470           0 :         cp_or_cv(:ncol,:) = cp_or_cv_dycore(:ncol,:,lchnk)
     471             :       else
     472           0 :         cp_or_cv(:ncol,:) = cpair-rair
     473             :       endif
     474           0 :       scaling(:,:)   = cpairv(:,:,lchnk)/cp_or_cv(:,:) !cp/cv scaling
     475           0 :       temp(1:ncol,:) = state%temp_ini(1:ncol,:)+scaling(1:ncol,:)*(state%T(1:ncol,:)-state%temp_ini(1:ncol,:))
     476           0 :       call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     477           0 :            state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     478           0 :            state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), temp(1:ncol,1:pver),        &
     479           0 :            vc_dycore, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),         &
     480           0 :            z_mid = state%z_ini(1:ncol,:),                                              &
     481           0 :            te = state%te_cur(1:ncol,dyn_te_idx), H2O = state%tw_cur(1:ncol,dyn_te_idx))
     482     1012392 :     else if (vc_dycore == vc_dry_pressure) then
     483             :       !
     484             :       ! SE specific hydrostatic energy
     485             :       !
     486     1012392 :       if (state%psetcols == pcols) then
     487  1573139448 :         cp_or_cv(:ncol,:) = cp_or_cv_dycore(:ncol,:,lchnk)
     488             :       else
     489           0 :         cp_or_cv(:ncol,:) = cpair
     490             :       endif
     491             :       !
     492             :       ! enthalpy scaling for energy consistency
     493             :       !
     494  1573139448 :       scaling(:ncol,:) = cpairv(:ncol,:,lchnk)/cp_or_cv_dycore(:ncol,:,lchnk)
     495  1573139448 :       temp(1:ncol,:)   = state%temp_ini(1:ncol,:)+scaling(1:ncol,:)*(state%T(1:ncol,:)-state%temp_ini(1:ncol,:))
     496     3037176 :       call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     497     2024784 :            state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     498     4049568 :            state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), temp(1:ncol,1:pver),        &
     499     2024784 :            vc_dry_pressure, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),   &
     500    12148704 :            te = state%te_cur(1:ncol,dyn_te_idx), H2O = state%tw_cur(1:ncol,dyn_te_idx))
     501             :     else
     502           0 :       state%te_cur(1:ncol,dyn_te_idx) = te(1:ncol)
     503           0 :       state%tw_cur(1:ncol,dyn_te_idx) = tw(1:ncol)
     504             :     end if
     505     1012392 :   end subroutine check_energy_chng
     506             : 
     507             : 
     508       32256 :   subroutine check_energy_gmean(state, pbuf2d, dtime, nstep)
     509             : 
     510     1012392 :     use physics_buffer, only : physics_buffer_desc, pbuf_get_field, pbuf_get_chunk
     511             :     use physics_types,   only: dyn_te_idx
     512             : !-----------------------------------------------------------------------
     513             : ! Compute global mean total energy of physics input and output states
     514             : ! computed consistently with dynamical core vertical coordinate
     515             : ! (under hydrostatic assumption)
     516             : !-----------------------------------------------------------------------
     517             : !------------------------------Arguments--------------------------------
     518             : 
     519             :     type(physics_state), intent(in   ), dimension(begchunk:endchunk) :: state
     520             :     type(physics_buffer_desc),    pointer    :: pbuf2d(:,:)
     521             : 
     522             :     real(r8), intent(in) :: dtime        ! physics time step
     523             :     integer , intent(in) :: nstep        ! current timestep number
     524             : 
     525             : !---------------------------Local storage-------------------------------
     526             :     integer :: ncol                      ! number of active columns
     527             :     integer :: lchnk                     ! chunk index
     528             : 
     529       32256 :     real(r8) :: te(pcols,begchunk:endchunk,4)
     530             :                                          ! total energy of input/output states (copy)
     531             :     real(r8) :: te_glob(4)               ! global means of total energy
     532       16128 :     real(r8), pointer :: teout(:)
     533             : !-----------------------------------------------------------------------
     534             : 
     535             :     ! Copy total energy out of input and output states
     536       81144 :     do lchnk = begchunk, endchunk
     537       65016 :        ncol = state(lchnk)%ncol
     538             :        ! input energy using dynamical core energy formula
     539     1085616 :        te(:ncol,lchnk,1) = state(lchnk)%te_ini(:ncol,dyn_te_idx)
     540             :        ! output energy
     541       65016 :        call pbuf_get_field(pbuf_get_chunk(pbuf2d,lchnk),teout_idx, teout)
     542             : 
     543     1085616 :        te(:ncol,lchnk,2) = teout(1:ncol)
     544             :        ! surface pressure for heating rate
     545     1085616 :        te(:ncol,lchnk,3) = state(lchnk)%pint(:ncol,pver+1)
     546             :        ! model top pressure for heating rate (not constant for z-based vertical coordinate!)
     547     1101744 :        te(:ncol,lchnk,4) = state(lchnk)%pint(:ncol,1)
     548             :     end do
     549             : 
     550             :     ! Compute global means of input and output energies and of
     551             :     ! surface pressure for heating rate (assume uniform ptop)
     552       16128 :     call gmean(te, te_glob, 4)
     553             : 
     554       16128 :     if (begchunk .le. endchunk) then
     555       16128 :        teinp_glob = te_glob(1)
     556       16128 :        teout_glob = te_glob(2)
     557       16128 :        psurf_glob = te_glob(3)
     558       16128 :        ptopb_glob = te_glob(4)
     559             : 
     560             :        ! Global mean total energy difference
     561       16128 :        tedif_glob =  teinp_glob - teout_glob
     562       16128 :        heat_glob  = -tedif_glob/dtime * gravit / (psurf_glob - ptopb_glob)
     563       16128 :        if (masterproc) then
     564          21 :           write(iulog,'(1x,a9,1x,i8,5(1x,e25.17))') "nstep, te", nstep, teinp_glob, teout_glob, &
     565          42 :                heat_glob, psurf_glob, ptopb_glob
     566             :        end if
     567             :     else
     568           0 :        heat_glob = 0._r8
     569             :     end if  !  (begchunk .le. endchunk)
     570             : 
     571       32256 :   end subroutine check_energy_gmean
     572             : 
     573             : !===============================================================================
     574     2730672 :   subroutine check_energy_fix(state, ptend, nstep, eshflx)
     575             : 
     576             : !-----------------------------------------------------------------------
     577             : ! Add heating rate required for global mean total energy conservation
     578             : !-----------------------------------------------------------------------
     579             : !------------------------------Arguments--------------------------------
     580             : 
     581             :     type(physics_state), intent(in   ) :: state
     582             :     type(physics_ptend), intent(out)   :: ptend
     583             : 
     584             :     integer , intent(in   ) :: nstep          ! time step number
     585             :     real(r8), intent(out  ) :: eshflx(pcols)  ! effective sensible heat flux
     586             : 
     587             : !---------------------------Local storage-------------------------------
     588             :     integer  :: i                        ! column
     589             :     integer  :: ncol                     ! number of atmospheric columns in chunk
     590             : !-----------------------------------------------------------------------
     591       65016 :     ncol = state%ncol
     592             : 
     593       65016 :     call physics_ptend_init(ptend, state%psetcols, 'chkenergyfix', ls=.true.)
     594             : 
     595             : #if ( defined OFFLINE_DYN )
     596             :     ! disable the energy fix for offline driver
     597             :     heat_glob = 0._r8
     598             : #endif
     599             : ! add (-) global mean total energy difference as heating
     600   101027304 :     ptend%s(:ncol,:pver) = heat_glob
     601             : 
     602             : ! compute effective sensible heat flux
     603     1085616 :     do i = 1, ncol
     604     1085616 :        eshflx(i) = heat_glob * (state%pint(i,pver+1) - state%pint(i,1)) * rga
     605             :     end do
     606             : 
     607       65016 :     return
     608       16128 :   end subroutine check_energy_fix
     609             : 
     610             : 
     611             : !===============================================================================
     612      123840 :   subroutine check_tracers_init(state, tracerint)
     613             : 
     614             : !-----------------------------------------------------------------------
     615             : ! Compute initial values of tracers integrals,
     616             : ! zero cumulative tendencies
     617             : !-----------------------------------------------------------------------
     618             : 
     619             : !------------------------------Arguments--------------------------------
     620             : 
     621             :     type(physics_state),   intent(in)    :: state
     622             :     type(check_tracers_data), intent(out)   :: tracerint
     623             : 
     624             : !---------------------------Local storage-------------------------------
     625             : 
     626             :     real(r8) :: tr(pcols)                          ! vertical integral of tracer
     627             :     real(r8) :: trpdel(pcols, pver)                ! pdel for tracer
     628             : 
     629             :     integer ncol                                   ! number of atmospheric columns
     630             :     integer  i,k,m                                 ! column, level,constituent indices
     631             :     integer :: ixcldice, ixcldliq                  ! CLDICE and CLDLIQ indices
     632             :     integer :: ixrain, ixsnow                      ! RAINQM and SNOWQM indices
     633             :     integer :: ixgrau                              ! GRAUQM index
     634             : !-----------------------------------------------------------------------
     635             : 
     636      123840 :     ncol  = state%ncol
     637      123840 :     call cnst_get_ind('CLDICE', ixcldice, abort=.false.)
     638      123840 :     call cnst_get_ind('CLDLIQ', ixcldliq, abort=.false.)
     639      123840 :     call cnst_get_ind('RAINQM', ixrain,   abort=.false.)
     640      123840 :     call cnst_get_ind('SNOWQM', ixsnow,   abort=.false.)
     641      123840 :     call cnst_get_ind('GRAUQM', ixgrau,   abort=.false.)
     642             : 
     643             : 
     644      123840 :     do m = 1,pcnst
     645             : 
     646      123840 :        if ( any(m == (/ 1, ixcldliq, ixcldice, &
     647             :                            ixrain,   ixsnow, ixgrau /)) ) exit   ! dont process water substances
     648             :                                                                  ! they are checked in check_energy
     649             : 
     650           0 :        if (cnst_get_type_byind(m).eq.'dry') then
     651           0 :           trpdel(:ncol,:) = state%pdeldry(:ncol,:)
     652             :        else
     653           0 :           trpdel(:ncol,:) = state%pdel(:ncol,:)
     654             :        endif
     655             : 
     656             :        ! Compute vertical integrals of tracer
     657           0 :        tr = 0._r8
     658           0 :        do k = 1, pver
     659           0 :           do i = 1, ncol
     660           0 :              tr(i) = tr(i) + state%q(i,k,m)*trpdel(i,k)*rga
     661             :           end do
     662             :        end do
     663             : 
     664             :        ! Compute vertical integrals of frozen static tracers and total water.
     665           0 :        do i = 1, ncol
     666           0 :           tracerint%tracer(i,m) = tr(i)
     667             :        end do
     668             : 
     669             :        ! zero cummulative boundary fluxes
     670           0 :        tracerint%tracer_tnd(:ncol,m) = 0._r8
     671             : 
     672           0 :        tracerint%count(m) = 0
     673             : 
     674             :     end do
     675             : 
     676      123840 :     return
     677             :   end subroutine check_tracers_init
     678             : 
     679             : !===============================================================================
     680      235296 :   subroutine check_tracers_chng(state, tracerint, name, nstep, ztodt, cflx)
     681             : 
     682             : !-----------------------------------------------------------------------
     683             : ! Check that the tracers and water change matches the boundary fluxes
     684             : ! these checks are not save when there are tracers transformations, as
     685             : ! they only check to see whether a mass change in the column is
     686             : ! associated with a flux
     687             : !-----------------------------------------------------------------------
     688             : 
     689             :     use cam_abortutils, only: endrun
     690             : 
     691             : 
     692             :     implicit none
     693             : 
     694             : !------------------------------Arguments--------------------------------
     695             : 
     696             :     type(physics_state)    , intent(in   ) :: state
     697             :     type(check_tracers_data), intent(inout) :: tracerint! tracers integrals and boundary fluxes
     698             :     character*(*),intent(in) :: name               ! parameterization name for fluxes
     699             :     integer , intent(in   ) :: nstep               ! current timestep number
     700             :     real(r8), intent(in   ) :: ztodt               ! 2 delta t (model time increment)
     701             :     real(r8), intent(in   ) :: cflx(pcols,pcnst)       ! boundary flux of tracers       (kg/m2/s)
     702             : 
     703             : !---------------------------Local storage-------------------------------
     704             : 
     705             :     real(r8) :: tracer_inp(pcols,pcnst)                   ! total tracer of new (input) state
     706             :     real(r8) :: tracer_xpd(pcols,pcnst)                   ! expected value (w0 + dt*boundary_flux)
     707             :     real(r8) :: tracer_dif(pcols,pcnst)                   ! tracer_inp - original tracer
     708             :     real(r8) :: tracer_tnd(pcols,pcnst)                   ! tendency from last process
     709             :     real(r8) :: tracer_rer(pcols,pcnst)                   ! relative error in tracer column
     710             : 
     711             :     real(r8) :: tr(pcols)                           ! vertical integral of tracer
     712             :     real(r8) :: trpdel(pcols, pver)                       ! pdel for tracer
     713             : 
     714             :     integer lchnk                                  ! chunk identifier
     715             :     integer ncol                                   ! number of atmospheric columns
     716             :     integer  i,k                                   ! column, level indices
     717             :     integer :: ixcldice, ixcldliq                  ! CLDICE and CLDLIQ indices
     718             :     integer :: ixrain, ixsnow                      ! RAINQM and SNOWQM indices
     719             :     integer :: ixgrau                              ! GRAUQM index
     720             :     integer :: m                            ! tracer index
     721             :     character(len=8) :: tracname   ! tracername
     722             : !-----------------------------------------------------------------------
     723             : 
     724      235296 :     lchnk = state%lchnk
     725      235296 :     ncol  = state%ncol
     726      235296 :     call cnst_get_ind('CLDICE', ixcldice, abort=.false.)
     727      235296 :     call cnst_get_ind('CLDLIQ', ixcldliq, abort=.false.)
     728      235296 :     call cnst_get_ind('RAINQM', ixrain,   abort=.false.)
     729      235296 :     call cnst_get_ind('SNOWQM', ixsnow,   abort=.false.)
     730      235296 :     call cnst_get_ind('GRAUQM', ixgrau,   abort=.false.)
     731             : 
     732      235296 :     do m = 1,pcnst
     733             : 
     734      235296 :        if ( any(m == (/ 1, ixcldliq, ixcldice, &
     735             :                            ixrain,   ixsnow, ixgrau /)) ) exit   ! dont process water substances
     736             :                                                                  ! they are checked in check_energy
     737           0 :        tracname = cnst_name(m)
     738           0 :        if (cnst_get_type_byind(m).eq.'dry') then
     739           0 :           trpdel(:ncol,:) = state%pdeldry(:ncol,:)
     740             :        else
     741           0 :           trpdel(:ncol,:) = state%pdel(:ncol,:)
     742             :        endif
     743             : 
     744             :        ! Compute vertical integrals tracers
     745           0 :        tr = 0._r8
     746           0 :        do k = 1, pver
     747           0 :           do i = 1, ncol
     748           0 :              tr(i) = tr(i) + state%q(i,k,m)*trpdel(i,k)*rga
     749             :           end do
     750             :        end do
     751             : 
     752             :        ! Compute vertical integrals of tracer
     753           0 :        do i = 1, ncol
     754           0 :           tracer_inp(i,m) = tr(i)
     755             :        end do
     756             : 
     757             :        ! compute expected values and tendencies
     758           0 :        do i = 1, ncol
     759             :           ! change in tracers
     760           0 :           tracer_dif(i,m) = tracer_inp(i,m) - tracerint%tracer(i,m)
     761             : 
     762             :           ! expected tendencies from boundary fluxes for last process
     763           0 :           tracer_tnd(i,m) = cflx(i,m)
     764             : 
     765             :           ! cummulative tendencies from boundary fluxes
     766           0 :           tracerint%tracer_tnd(i,m) = tracerint%tracer_tnd(i,m) + tracer_tnd(i,m)
     767             : 
     768             :           ! expected new values from original values plus boundary fluxes
     769           0 :           tracer_xpd(i,m) = tracerint%tracer(i,m) + tracerint%tracer_tnd(i,m)*ztodt
     770             : 
     771             :           ! relative error, expected value - input value / original
     772           0 :           tracer_rer(i,m) = (tracer_xpd(i,m) - tracer_inp(i,m)) / tracerint%tracer(i,m)
     773             :        end do
     774             : 
     775             : !! final loop for error checking
     776             : !    do i = 1, ncol
     777             : 
     778             : !! error messages
     779             : !       if (abs(enrgy_rer(i)) > 1.E-14 .or. abs(water_rer(i)) > 1.E-14) then
     780             : !          tracerint%count = tracerint%count + 1
     781             : !          write(iulog,*) "significant conservations error after ", name,        &
     782             : !               " count", tracerint%count, " nstep", nstep, "chunk", lchnk, "col", i
     783             : !          write(iulog,*) enrgy_inp(i),enrgy_xpd(i),enrgy_dif(i),tracerint%enrgy_tnd(i)*ztodt,  &
     784             : !               enrgy_tnd(i)*ztodt,enrgy_rer(i)
     785             : !          write(iulog,*) water_inp(i),water_xpd(i),water_dif(i),tracerint%water_tnd(i)*ztodt,  &
     786             : !               water_tnd(i)*ztodt,water_rer(i)
     787             : !       end if
     788             : !    end do
     789             : 
     790             : 
     791             :        ! final loop for error checking
     792           0 :        if ( maxval(tracer_rer) > 1.E-14_r8 ) then
     793           0 :           write(iulog,*) "CHECK_TRACERS TRACER large rel error"
     794           0 :           write(iulog,*) tracer_rer
     795             :        endif
     796             : 
     797           0 :        do i = 1, ncol
     798             :           ! error messages
     799           0 :           if (abs(tracer_rer(i,m)) > 1.E-14_r8 ) then
     800           0 :              tracerint%count = tracerint%count + 1
     801           0 :              write(iulog,*) "CHECK_TRACERS TRACER significant conservation error after ", name,        &
     802           0 :                   " count", tracerint%count, " nstep", nstep, "chunk", lchnk, "col",i
     803           0 :              write(iulog,*)' process name, tracname, index ',  name, tracname, m
     804           0 :              write(iulog,*)" input integral              ",tracer_inp(i,m)
     805           0 :              write(iulog,*)" expected integral           ", tracer_xpd(i,m)
     806           0 :              write(iulog,*)" input - inital integral     ",tracer_dif(i,m)
     807           0 :              write(iulog,*)" cumulative tend      ",tracerint%tracer_tnd(i,m)*ztodt
     808           0 :              write(iulog,*)" process tend         ",tracer_tnd(i,m)*ztodt
     809           0 :              write(iulog,*)" relative error       ",tracer_rer(i,m)
     810           0 :              call endrun()
     811             :           end if
     812             :        end do
     813             :     end do
     814             : 
     815      235296 :     return
     816             :   end subroutine check_tracers_chng
     817             : 
     818             : !#######################################################################
     819             : 
     820      377712 :   subroutine tot_energy_phys(state, outfld_name_suffix,vc)
     821             :     use physconst,       only: rga,rearth,omega
     822             :     use cam_thermo,      only: get_hydrostatic_energy,thermo_budget_num_vars,thermo_budget_vars, &
     823             :                                wvidx,wlidx,wiidx,seidx,poidx,keidx,moidx,mridx,ttidx,teidx
     824             :     use cam_history,     only: outfld
     825             :     use dyn_tests_utils, only: vc_physics, vc_height, vc_dry_pressure
     826             : 
     827             :     use cam_abortutils,  only: endrun
     828             :     use cam_history_support, only: max_fieldname_len
     829             :     use cam_budget,      only: thermo_budget_history
     830             : !------------------------------Arguments--------------------------------
     831             : 
     832             :     type(physics_state), intent(inout) :: state
     833             :     character(len=*),    intent(in)    :: outfld_name_suffix ! suffix for "outfld"
     834             :     integer, optional,   intent(in)    :: vc                 ! vertical coordinate
     835             : 
     836             : !---------------------------Local storage-------------------------------
     837             :     real(r8) :: se(pcols)                          ! Dry Static energy (J/m2)
     838             :     real(r8) :: po(pcols)                          ! surface potential or potential energy (J/m2)
     839             :     real(r8) :: ke(pcols)                          ! kinetic energy    (J/m2)
     840             :     real(r8) :: wv(pcols)                          ! column integrated vapor       (kg/m2)
     841             :     real(r8) :: liq(pcols)                         ! column integrated liquid      (kg/m2)
     842             :     real(r8) :: ice(pcols)                         ! column integrated ice         (kg/m2)
     843             :     real(r8) :: tt(pcols)                          ! column integrated test tracer (kg/m2)
     844             :     real(r8) :: mr(pcols)                          ! column integrated wind axial angular momentum (kg*m2/s)
     845             :     real(r8) :: mo(pcols)                          ! column integrated mass axial angular momentum (kg*m2/s)
     846             :     real(r8) :: tt_tmp,mr_tmp,mo_tmp,cos_lat
     847             :     real(r8) :: mr_cnst, mo_cnst
     848             :     real(r8) :: cp_or_cv(pcols,pver)               ! cp for pressure-based vcoord and cv for height vcoord
     849             :     real(r8) :: temp(pcols,pver)                   ! temperature
     850             :     real(r8) :: scaling(pcols,pver)                ! scaling for conversion of temperature increment
     851             : 
     852             :     integer :: lchnk                               ! chunk identifier
     853             :     integer :: ncol                                ! number of atmospheric columns
     854             :     integer :: i,k                                 ! column, level indices
     855             :     integer :: vc_loc                              ! local vertical coordinate variable
     856             :     integer :: ixtt                                ! test tracer index
     857             :     character(len=max_fieldname_len) :: name_out(thermo_budget_num_vars)
     858             : 
     859             : !-----------------------------------------------------------------------
     860             : 
     861      377712 :     if (.not.thermo_budget_history) return
     862             : 
     863           0 :     do i=1,thermo_budget_num_vars
     864           0 :        name_out(i)=trim(thermo_budget_vars(i))//'_'//trim(outfld_name_suffix)
     865             :     end do
     866             : 
     867           0 :     lchnk = state%lchnk
     868           0 :     ncol  = state%ncol
     869             : 
     870           0 :     if (present(vc)) then
     871           0 :       vc_loc = vc
     872             :     else
     873           0 :       vc_loc = vc_physics
     874             :     end if
     875             : 
     876           0 :     if (state%psetcols == pcols) then
     877           0 :       if (vc_loc == vc_height .or. vc_loc == vc_dry_pressure) then
     878           0 :         cp_or_cv(:ncol,:) = cp_or_cv_dycore(:ncol,:,lchnk)
     879             :       else
     880           0 :         cp_or_cv(:ncol,:) = cpairv(:ncol,:,lchnk)
     881             :       end if
     882             :     else
     883           0 :       call endrun('tot_energy_phys: energy diagnostics not implemented/tested for subcolumns')
     884             :     end if
     885             : 
     886           0 :     if (vc_loc == vc_height .or. vc_loc == vc_dry_pressure) then
     887           0 :       scaling(:ncol,:) = cpairv(:ncol,:,lchnk)/cp_or_cv(:ncol,:)!scaling for energy consistency
     888             :     else
     889           0 :       scaling(:ncol,:) = 1.0_r8 !internal energy / enthalpy same as CAM physics
     890             :     end if
     891             :     ! scale accumulated temperature increment for internal energy / enthalpy consistency
     892           0 :     temp(1:ncol,:) = state%temp_ini(1:ncol,:)+scaling(1:ncol,:)*(state%T(1:ncol,:)- state%temp_ini(1:ncol,:))
     893           0 :     call get_hydrostatic_energy(state%q(1:ncol,1:pver,1:pcnst),.true.,               &
     894           0 :          state%pdel(1:ncol,1:pver), cp_or_cv(1:ncol,1:pver),                         &
     895           0 :          state%u(1:ncol,1:pver), state%v(1:ncol,1:pver), temp(1:ncol,1:pver),        &
     896           0 :          vc_loc, ptop=state%pintdry(1:ncol,1), phis = state%phis(1:ncol),            &
     897           0 :          z_mid = state%z_ini(1:ncol,:), se = se(1:ncol),                             &
     898             :          po = po(1:ncol), ke = ke(1:ncol), wv = wv(1:ncol), liq = liq(1:ncol),       &
     899           0 :          ice = ice(1:ncol))
     900             : 
     901           0 :     call cnst_get_ind('TT_LW' , ixtt    , abort=.false.)
     902           0 :     tt    = 0._r8
     903           0 :     if (ixtt > 1) then
     904           0 :       if (name_out(ttidx) == 'TT_pAM'.or.name_out(ttidx) == 'TT_zAM') then
     905             :         !
     906             :         ! after dme_adjust mixing ratios are all wet
     907             :         !
     908           0 :         do k = 1, pver
     909           0 :           do i = 1, ncol
     910           0 :             tt_tmp   = state%q(i,k,ixtt)*state%pdel(i,k)*rga
     911           0 :             tt   (i) = tt(i)    + tt_tmp
     912             :           end do
     913             :         end do
     914             :       else
     915           0 :         do k = 1, pver
     916           0 :           do i = 1, ncol
     917           0 :             tt_tmp   = state%q(i,k,ixtt)*state%pdeldry(i,k)*rga
     918           0 :             tt   (i) = tt(i)    + tt_tmp
     919             :           end do
     920             :         end do
     921             :       end if
     922             :     end if
     923             : 
     924           0 :     call outfld(name_out(seidx)  ,se      , pcols   ,lchnk   )
     925           0 :     call outfld(name_out(poidx)  ,po      , pcols   ,lchnk   )
     926           0 :     call outfld(name_out(keidx)  ,ke      , pcols   ,lchnk   )
     927           0 :     call outfld(name_out(wvidx)  ,wv      , pcols   ,lchnk   )
     928           0 :     call outfld(name_out(wlidx)  ,liq     , pcols   ,lchnk   )
     929           0 :     call outfld(name_out(wiidx)  ,ice     , pcols   ,lchnk   )
     930           0 :     call outfld(name_out(ttidx)  ,tt      , pcols   ,lchnk   )
     931           0 :     call outfld(name_out(teidx)  ,se+ke+po, pcols   ,lchnk   )
     932             :     !
     933             :     ! Axial angular momentum diagnostics
     934             :     !
     935             :     ! Code follows
     936             :     !
     937             :     ! Lauritzen et al., (2014): Held-Suarez simulations with the Community Atmosphere Model
     938             :     ! Spectral Element (CAM-SE) dynamical core: A global axial angularmomentum analysis using Eulerian
     939             :     ! and floating Lagrangian vertical coordinates. J. Adv. Model. Earth Syst. 6,129-140,
     940             :     ! doi:10.1002/2013MS000268
     941             :     !
     942             :     ! MR is equation (6) without \Delta A and sum over areas (areas are in units of radians**2)
     943             :     ! MO is equation (7) without \Delta A and sum over areas (areas are in units of radians**2)
     944             :     !
     945             :     
     946           0 :     mr_cnst = rga*rearth**3
     947           0 :     mo_cnst = rga*omega*rearth**4
     948             :     
     949           0 :     mr = 0.0_r8
     950           0 :     mo = 0.0_r8
     951           0 :     do k = 1, pver
     952           0 :        do i = 1, ncol
     953           0 :           cos_lat = cos(state%lat(i))
     954           0 :           mr_tmp = mr_cnst*state%u(i,k)*state%pdel(i,k)*cos_lat
     955           0 :           mo_tmp = mo_cnst*state%pdel(i,k)*cos_lat**2
     956             :           
     957           0 :           mr(i) = mr(i) + mr_tmp
     958           0 :           mo(i) = mo(i) + mo_tmp
     959             :        end do
     960             :     end do
     961             :     
     962           0 :     call outfld(name_out(mridx)  ,mr, pcols,lchnk   )
     963           0 :     call outfld(name_out(moidx)  ,mo, pcols,lchnk   )
     964             : 
     965      377712 :   end subroutine tot_energy_phys
     966             : 
     967             : 
     968      377712 : end module check_energy

Generated by: LCOV version 1.14