From 6d6168969c332944b327b1ab4056b7c1ebf58e56 Mon Sep 17 00:00:00 2001 From: Peter Hjort Lauritzen Date: Wed, 8 Jul 2026 11:15:43 -0600 Subject: [PATCH] Remove defunct tracer mass-conservation check The check has been a no-op since CAM6 ('exit' instead of 'cycle' terminated the constituent loop on water vapor; see issue #1592). Activating it reveals the design is not valid: individual chemical species are not conserved under reactions (only the column sum is). --- doc/ChangeLog | 24 ++++ src/physics/cam/check_energy.F90 | 222 +------------------------------ src/physics/cam/physpkg.F90 | 23 ---- src/physics/cam7/physpkg.F90 | 23 ---- src/physics/simple/physpkg.F90 | 9 -- 5 files changed, 25 insertions(+), 276 deletions(-) diff --git a/doc/ChangeLog b/doc/ChangeLog index 087856c5b8..3a5d190745 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,29 @@ =============================================================== +Tag name: cam6_4_186 +Originator(s): pel +Date: 8 Jul 2026 +One-line Summary: Remove defunct tracer mass-conservation check +Github PR URL: https://github.com/ESCOMP/CAM/pull/??? + +Purpose of changes (include the issue number and title text for each relevant GitHub issue): + + Remove check_tracers_init/check_tracers_chng and the check_tracers_data type + (https://github.com/ESCOMP/CAM/issues/1592: check_energy tracer mass-conservation + check never runs: 'exit' instead of 'cycle'). The check has been a no-op since + CAM6, and its design is not valid: individual chemical species are not conserved + under reactions (only the column sum is). + +List all existing files that have been modified, and describe the changes: + +M src/physics/cam/check_energy.F90 +M src/physics/cam/physpkg.F90 +M src/physics/cam7/physpkg.F90 +M src/physics/simple/physpkg.F90 + - remove defunct tracer mass-conservation check and its data structure + +=============================================================== + Tag name: cam6_4_185 Originator(s): fvitt Date: 29 Jun 2026 diff --git a/src/physics/cam/check_energy.F90 b/src/physics/cam/check_energy.F90 index d1d59e173f..7959f58c79 100644 --- a/src/physics/cam/check_energy.F90 +++ b/src/physics/cam/check_energy.F90 @@ -24,21 +24,15 @@ module check_energy use ppgrid, only: pcols, pver use spmd_utils, only: masterproc - use physconst, only: rga use air_composition, only: cpairv, cp_or_cv_dycore use physics_types, only: physics_state - use constituents, only: cnst_get_ind, pcnst, cnst_name, cnst_get_type_byind + use constituents, only: cnst_get_ind, pcnst use cam_logfile, only: iulog implicit none private - ! Public types: - public check_tracers_data - ! Public methods - not CCPP-ized - public :: check_tracers_init ! initialize tracer integrals and cumulative boundary fluxes - public :: check_tracers_chng ! check changes in integrals against cumulative boundary fluxes public :: tot_energy_phys ! calculate and output total energy and axial angular momentum diagnostics ! These subroutines cannot be CCPP-ized @@ -73,13 +67,6 @@ module check_energy integer, public :: ducore_idx = 0 ! ducore index in physics buffer integer, public :: dvcore_idx = 0 ! dvcore index in physics buffer - type check_tracers_data - real(r8) :: tracer(pcols,pcnst) ! initial vertically integrated total (kinetic + static) energy - real(r8) :: tracer_tnd(pcols,pcnst) ! cumulative boundary flux of total energy - integer :: count(pcnst) ! count of values with significant imbalances - end type check_tracers_data - - !=============================================================================== contains !=============================================================================== @@ -201,213 +188,6 @@ subroutine check_energy_init() end subroutine check_energy_init -!=============================================================================== - subroutine check_tracers_init(state, tracerint) - -!----------------------------------------------------------------------- -! Compute initial values of tracers integrals, -! zero cumulative tendencies -!----------------------------------------------------------------------- - -!------------------------------Arguments-------------------------------- - - type(physics_state), intent(in) :: state - type(check_tracers_data), intent(out) :: tracerint - -!---------------------------Local storage------------------------------- - - real(r8) :: tr(pcols) ! vertical integral of tracer - real(r8) :: trpdel(pcols, pver) ! pdel for tracer - - integer ncol ! number of atmospheric columns - integer i,k,m ! column, level,constituent indices - integer :: ixcldice, ixcldliq ! CLDICE and CLDLIQ indices - integer :: ixrain, ixsnow ! RAINQM and SNOWQM indices - integer :: ixgrau ! GRAUQM index -!----------------------------------------------------------------------- - - ncol = state%ncol - call cnst_get_ind('CLDICE', ixcldice, abort=.false.) - call cnst_get_ind('CLDLIQ', ixcldliq, abort=.false.) - call cnst_get_ind('RAINQM', ixrain, abort=.false.) - call cnst_get_ind('SNOWQM', ixsnow, abort=.false.) - call cnst_get_ind('GRAUQM', ixgrau, abort=.false.) - - - do m = 1,pcnst - - if ( any(m == (/ 1, ixcldliq, ixcldice, & - ixrain, ixsnow, ixgrau /)) ) exit ! dont process water substances - ! they are checked in check_energy - - if (cnst_get_type_byind(m).eq.'dry') then - trpdel(:ncol,:) = state%pdeldry(:ncol,:) - else - trpdel(:ncol,:) = state%pdel(:ncol,:) - endif - - ! Compute vertical integrals of tracer - tr = 0._r8 - do k = 1, pver - do i = 1, ncol - tr(i) = tr(i) + state%q(i,k,m)*trpdel(i,k)*rga - end do - end do - - ! Compute vertical integrals of frozen static tracers and total water. - do i = 1, ncol - tracerint%tracer(i,m) = tr(i) - end do - - ! zero cummulative boundary fluxes - tracerint%tracer_tnd(:ncol,m) = 0._r8 - - tracerint%count(m) = 0 - - end do - - return - end subroutine check_tracers_init - -!=============================================================================== - subroutine check_tracers_chng(state, tracerint, name, nstep, ztodt, cflx) - -!----------------------------------------------------------------------- -! Check that the tracers and water change matches the boundary fluxes -! these checks are not save when there are tracers transformations, as -! they only check to see whether a mass change in the column is -! associated with a flux -!----------------------------------------------------------------------- - - use cam_abortutils, only: endrun - - - implicit none - -!------------------------------Arguments-------------------------------- - - type(physics_state) , intent(in ) :: state - type(check_tracers_data), intent(inout) :: tracerint! tracers integrals and boundary fluxes - character*(*),intent(in) :: name ! parameterization name for fluxes - integer , intent(in ) :: nstep ! current timestep number - real(r8), intent(in ) :: ztodt ! 2 delta t (model time increment) - real(r8), intent(in ) :: cflx(pcols,pcnst) ! boundary flux of tracers (kg/m2/s) - -!---------------------------Local storage------------------------------- - - real(r8) :: tracer_inp(pcols,pcnst) ! total tracer of new (input) state - real(r8) :: tracer_xpd(pcols,pcnst) ! expected value (w0 + dt*boundary_flux) - real(r8) :: tracer_dif(pcols,pcnst) ! tracer_inp - original tracer - real(r8) :: tracer_tnd(pcols,pcnst) ! tendency from last process - real(r8) :: tracer_rer(pcols,pcnst) ! relative error in tracer column - - real(r8) :: tr(pcols) ! vertical integral of tracer - real(r8) :: trpdel(pcols, pver) ! pdel for tracer - - integer lchnk ! chunk identifier - integer ncol ! number of atmospheric columns - integer i,k ! column, level indices - integer :: ixcldice, ixcldliq ! CLDICE and CLDLIQ indices - integer :: ixrain, ixsnow ! RAINQM and SNOWQM indices - integer :: ixgrau ! GRAUQM index - integer :: m ! tracer index - character(len=8) :: tracname ! tracername -!----------------------------------------------------------------------- - - lchnk = state%lchnk - ncol = state%ncol - call cnst_get_ind('CLDICE', ixcldice, abort=.false.) - call cnst_get_ind('CLDLIQ', ixcldliq, abort=.false.) - call cnst_get_ind('RAINQM', ixrain, abort=.false.) - call cnst_get_ind('SNOWQM', ixsnow, abort=.false.) - call cnst_get_ind('GRAUQM', ixgrau, abort=.false.) - - do m = 1,pcnst - - if ( any(m == (/ 1, ixcldliq, ixcldice, & - ixrain, ixsnow, ixgrau /)) ) exit ! dont process water substances - ! they are checked in check_energy - tracname = cnst_name(m) - if (cnst_get_type_byind(m).eq.'dry') then - trpdel(:ncol,:) = state%pdeldry(:ncol,:) - else - trpdel(:ncol,:) = state%pdel(:ncol,:) - endif - - ! Compute vertical integrals tracers - tr = 0._r8 - do k = 1, pver - do i = 1, ncol - tr(i) = tr(i) + state%q(i,k,m)*trpdel(i,k)*rga - end do - end do - - ! Compute vertical integrals of tracer - do i = 1, ncol - tracer_inp(i,m) = tr(i) - end do - - ! compute expected values and tendencies - do i = 1, ncol - ! change in tracers - tracer_dif(i,m) = tracer_inp(i,m) - tracerint%tracer(i,m) - - ! expected tendencies from boundary fluxes for last process - tracer_tnd(i,m) = cflx(i,m) - - ! cummulative tendencies from boundary fluxes - tracerint%tracer_tnd(i,m) = tracerint%tracer_tnd(i,m) + tracer_tnd(i,m) - - ! expected new values from original values plus boundary fluxes - tracer_xpd(i,m) = tracerint%tracer(i,m) + tracerint%tracer_tnd(i,m)*ztodt - - ! relative error, expected value - input value / original - tracer_rer(i,m) = (tracer_xpd(i,m) - tracer_inp(i,m)) / tracerint%tracer(i,m) - end do - -!! final loop for error checking -! do i = 1, ncol - -!! error messages -! if (abs(enrgy_rer(i)) > 1.E-14 .or. abs(water_rer(i)) > 1.E-14) then -! tracerint%count = tracerint%count + 1 -! write(iulog,*) "significant conservations error after ", name, & -! " count", tracerint%count, " nstep", nstep, "chunk", lchnk, "col", i -! write(iulog,*) enrgy_inp(i),enrgy_xpd(i),enrgy_dif(i),tracerint%enrgy_tnd(i)*ztodt, & -! enrgy_tnd(i)*ztodt,enrgy_rer(i) -! write(iulog,*) water_inp(i),water_xpd(i),water_dif(i),tracerint%water_tnd(i)*ztodt, & -! water_tnd(i)*ztodt,water_rer(i) -! end if -! end do - - - ! final loop for error checking - if ( maxval(tracer_rer) > 1.E-14_r8 ) then - write(iulog,*) "CHECK_TRACERS TRACER large rel error" - write(iulog,*) tracer_rer - endif - - do i = 1, ncol - ! error messages - if (abs(tracer_rer(i,m)) > 1.E-14_r8 ) then - tracerint%count = tracerint%count + 1 - write(iulog,*) "CHECK_TRACERS TRACER significant conservation error after ", name, & - " count", tracerint%count, " nstep", nstep, "chunk", lchnk, "col",i - write(iulog,*)' process name, tracname, index ', name, tracname, m - write(iulog,*)" input integral ",tracer_inp(i,m) - write(iulog,*)" expected integral ", tracer_xpd(i,m) - write(iulog,*)" input - inital integral ",tracer_dif(i,m) - write(iulog,*)" cumulative tend ",tracerint%tracer_tnd(i,m)*ztodt - write(iulog,*)" process tend ",tracer_tnd(i,m)*ztodt - write(iulog,*)" relative error ",tracer_rer(i,m) - call endrun() - end if - end do - end do - - return - end subroutine check_tracers_chng - !####################################################################### subroutine tot_energy_phys(state, outfld_name_suffix,vc) diff --git a/src/physics/cam/physpkg.F90 b/src/physics/cam/physpkg.F90 index 3d6d260b23..968966e660 100644 --- a/src/physics/cam/physpkg.F90 +++ b/src/physics/cam/physpkg.F90 @@ -1409,7 +1409,6 @@ subroutine tphysac (ztodt, cam_in, & use carma_intr, only: carma_emission_tend, carma_timestep_tend use carma_flags_mod, only: carma_do_aerosol, carma_do_emission use check_energy, only: tot_energy_phys - use check_energy, only: check_tracers_data, check_tracers_init, check_tracers_chng use check_energy, only: check_energy_cam_chng use time_manager, only: get_nstep use cam_abortutils, only: endrun @@ -1444,9 +1443,6 @@ subroutine tphysac (ztodt, cam_in, & type(physics_tend ), intent(inout) :: tend type(physics_buffer_desc), pointer :: pbuf(:) - - type(check_tracers_data):: tracerint ! tracer mass integrals and cummulative boundary fluxes - ! !---------------------------Local workspace----------------------------- ! @@ -1582,7 +1578,6 @@ subroutine tphysac (ztodt, cam_in, & ! get nstep and zero array for energy checker zero = 0._r8 nstep = get_nstep() - call check_tracers_init(state, tracerint) ! Check if latent heat flux exceeds the total moisture content of the ! lowest model layer, thereby creating negative moisture. @@ -1612,8 +1607,6 @@ subroutine tphysac (ztodt, cam_in, & call cam_snapshot_all_outfld_tphysac(cam_snapshot_after_num, state, tend, cam_in, cam_out, pbuf,& fh2o, surfric, obklen, flx_heat) end if - call check_tracers_chng(state, tracerint, "aoa_tracers_timestep_tend", nstep, ztodt, & - cam_in%cflx) if (trim(cam_take_snapshot_before) == "co2_cycle_set_ptend") then call cam_snapshot_all_outfld_tphysac(cam_snapshot_before_num, state, tend, cam_in, cam_out, pbuf,& @@ -1666,8 +1659,6 @@ subroutine tphysac (ztodt, cam_in, & fh2o, surfric, obklen, flx_heat) end if call check_energy_cam_chng(state, tend, "chem", nstep, ztodt, fh2o, zero, zero, zero) - call check_tracers_chng(state, tracerint, "chem_timestep_tend", nstep, ztodt, & - cam_in%cflx) end if call t_stopf('adv_tracer_src_snk') @@ -1786,8 +1777,6 @@ subroutine tphysac (ztodt, cam_in, & zero, cam_in%shf) endif - call check_tracers_chng(state, tracerint, "vdiff", nstep, ztodt, cam_in%cflx) - ! aerosol dry deposition processes call t_startf('aero_drydep') @@ -2162,7 +2151,6 @@ subroutine tphysbc (ztodt, state, & use convect_shallow, only: convect_shallow_tend use check_energy, only: check_energy_timestep_init, check_energy_cam_chng use check_energy, only: check_energy_cam_fix - use check_energy, only: check_tracers_data, check_tracers_init, check_tracers_chng use check_energy, only: tot_energy_phys use dycore, only: dycore_is use aero_model, only: aero_model_wetdep @@ -2298,8 +2286,6 @@ subroutine tphysbc (ztodt, state, & real(r8) :: det_ice(pcols) ! vertical integral of detrained ice real(r8) :: flx_cnd(pcols) real(r8) :: flx_heat(pcols) - type(check_tracers_data):: tracerint ! energy integrals and cummulative boundary fluxes - real(r8) :: zero_tracers(pcols,pcnst) ! For abstract aerosol interface (calcsize/wateruptake) class(aerosol_properties), pointer :: aero_props @@ -2323,7 +2309,6 @@ subroutine tphysbc (ztodt, state, & call t_startf('bc_init') zero = 0._r8 - zero_tracers(:,:) = 0._r8 zero_sc(:) = 0._r8 lchnk = state%lchnk @@ -2380,9 +2365,6 @@ subroutine tphysbc (ztodt, state, & ! call diag_state_b4_phys_write (state) - ! compute mass integrals of input tracers state - call check_tracers_init(state, tracerint) - call t_stopf('bc_init') ! Zero-initialize subroutine-level variables for snapshot @@ -2584,8 +2566,6 @@ subroutine tphysbc (ztodt, state, & flx_cnd(:ncol) = prec_sh(:ncol) + rliq2(:ncol) call check_energy_cam_chng(state, tend, "convect_shallow", nstep, ztodt, zero, flx_cnd, snow_sh, zero) - call check_tracers_chng(state, tracerint, "convect_shallow", nstep, ztodt, zero_tracers) - call t_stopf('moist_convection') ! Rebin the 4-bin version of sea salt into bins for coarse and accumulation @@ -3025,9 +3005,6 @@ subroutine tphysbc (ztodt, state, & call physics_update(state, ptend, ztodt, tend) call t_stopf ('convect_deep_tend2') - ! check tracer integrals - call check_tracers_chng(state, tracerint, "cmfmca", nstep, ztodt, zero_tracers) - call t_stopf('aerosol_wet_processes') endif diff --git a/src/physics/cam7/physpkg.F90 b/src/physics/cam7/physpkg.F90 index 78d1deba92..f3862f60c7 100644 --- a/src/physics/cam7/physpkg.F90 +++ b/src/physics/cam7/physpkg.F90 @@ -1404,7 +1404,6 @@ subroutine tphysac (ztodt, cam_in, & use aero_model, only: aero_model_drydep use check_energy, only: check_energy_timestep_init, check_energy_cam_chng use check_energy, only: tot_energy_phys - use check_energy, only: check_tracers_data, check_tracers_init, check_tracers_chng use time_manager, only: get_nstep use cam_abortutils, only: endrun use dycore, only: dycore_is @@ -1465,9 +1464,6 @@ subroutine tphysac (ztodt, cam_in, & type(physics_tend ), intent(inout) :: tend type(physics_buffer_desc), pointer :: pbuf(:) - - type(check_tracers_data):: tracerint ! tracer mass integrals and cummulative boundary fluxes - ! !---------------------------Local workspace----------------------------- ! @@ -1505,7 +1501,6 @@ subroutine tphysac (ztodt, cam_in, & real(r8) :: flx_cnd(pcols) real(r8) :: zero_sc(pcols*psubcols) ! array of zeros - real(r8) :: zero_tracers(pcols,pcnst) real(r8), pointer :: dlfzm(:,:) ! ZM detrained convective cloud water mixing ratio. real(r8), pointer :: cmfmczm(:,:) ! ZM convective mass fluxes @@ -1685,9 +1680,7 @@ subroutine tphysac (ztodt, cam_in, & ! get nstep and zero array for energy checker zero = 0._r8 zero_sc(:) = 0._r8 - zero_tracers(:,:) = 0._r8 nstep = get_nstep() - call check_tracers_init(state, tracerint) ! Check if latent heat flux exceeds the total moisture content of the ! lowest model layer, thereby creating negative moisture. @@ -2051,9 +2044,6 @@ subroutine tphysac (ztodt, cam_in, & call physics_update(state, ptend, ztodt, tend) call t_stopf ('convect_deep_tend2') - ! check tracer integrals - call check_tracers_chng(state, tracerint, "cmfmca", nstep, ztodt, zero_tracers) - call t_stopf('aerosol_wet_processes') endif @@ -2137,8 +2127,6 @@ subroutine tphysac (ztodt, cam_in, & call cam_snapshot_all_outfld_tphysac(cam_snapshot_after_num, state, tend, cam_in, cam_out, pbuf,& fh2o, surfric, obklen, flx_heat, cmfmc, dlf, det_s, det_ice, net_flx) end if - call check_tracers_chng(state, tracerint, "aoa_tracers_timestep_tend", nstep, ztodt, & - cam_in%cflx) if (trim(cam_take_snapshot_before) == "co2_cycle_set_ptend") then call cam_snapshot_all_outfld_tphysac(cam_snapshot_before_num, state, tend, cam_in, cam_out, pbuf,& @@ -2186,8 +2174,6 @@ subroutine tphysac (ztodt, cam_in, & fh2o, surfric, obklen, flx_heat, cmfmc, dlf, det_s, det_ice, net_flx) end if call check_energy_cam_chng(state, tend, "chem", nstep, ztodt, fh2o, zero, zero, zero) - call check_tracers_chng(state, tracerint, "chem_timestep_tend", nstep, ztodt, & - cam_in%cflx) end if call t_stopf('adv_tracer_src_snk') @@ -2303,8 +2289,6 @@ subroutine tphysac (ztodt, cam_in, & zero, cam_in%shf) endif - call check_tracers_chng(state, tracerint, "vdiff", nstep, ztodt, cam_in%cflx) - ! aerosol dry deposition processes call t_startf('aero_drydep') @@ -2658,7 +2642,6 @@ subroutine tphysbc (ztodt, state, & use time_manager, only: is_first_step, get_nstep use convect_diagnostics,only: convect_diagnostics_calc use check_energy, only: check_energy_cam_chng, check_energy_cam_fix - use check_energy, only: check_tracers_data, check_tracers_init use check_energy, only: tot_energy_phys use dycore, only: dycore_is use radiation, only: radiation_tend @@ -2762,8 +2745,6 @@ subroutine tphysbc (ztodt, state, & real(r8) :: rliq2(pcols) ! vertical integral of liquid from shallow scheme real(r8) :: flx_cnd(pcols) real(r8) :: flx_heat(pcols) - type(check_tracers_data):: tracerint ! energy integrals and cummulative boundary fluxes - real(r8) :: zero_tracers(pcols,pcnst) real(r8), pointer :: psl(:) ! Sea Level Pressure @@ -2783,7 +2764,6 @@ subroutine tphysbc (ztodt, state, & call t_startf('bc_init') zero = 0._r8 - zero_tracers(:,:) = 0._r8 zero_sc(:) = 0._r8 lchnk = state%lchnk @@ -2841,9 +2821,6 @@ subroutine tphysbc (ztodt, state, & ! call diag_state_b4_phys_write (state) - ! compute mass integrals of input tracers state - call check_tracers_init(state, tracerint) - call t_stopf('bc_init') ! Zero-initialize subroutine-level variables for snapshot diff --git a/src/physics/simple/physpkg.F90 b/src/physics/simple/physpkg.F90 index a0893b08f0..c490e32f8d 100644 --- a/src/physics/simple/physpkg.F90 +++ b/src/physics/simple/physpkg.F90 @@ -737,7 +737,6 @@ subroutine tphysbc (ztodt, state, tend, pbuf, cam_out, cam_in ) use time_manager, only: get_nstep use check_energy, only: check_energy_cam_chng, check_energy_cam_fix use check_energy, only: check_energy_timestep_init - use check_energy, only: check_tracers_data, check_tracers_init, check_tracers_chng use check_energy, only: tot_energy_phys use chemistry, only: chem_is_active, chem_timestep_tend use held_suarez_cam, only: held_suarez_tend @@ -784,7 +783,6 @@ subroutine tphysbc (ztodt, state, tend, pbuf, cam_out, cam_in ) real(r8) :: zero(pcols) ! array of zeros real(r8) :: flx_heat(pcols) - type(check_tracers_data) :: tracerint ! energy integrals and cummulative boundary fluxes !----------------------------------------------------------------------- call t_startf('bc_init') @@ -825,9 +823,6 @@ subroutine tphysbc (ztodt, state, tend, pbuf, cam_out, cam_in ) ! Dump out "before tphysbc" state call diag_state_b4_phys_write(state) - ! compute mass integrals of input tracers state - call check_tracers_init(state, tracerint) - call t_stopf('bc_init') !=================================================== @@ -981,8 +976,6 @@ subroutine tphysbc (ztodt, state, tend, pbuf, cam_out, cam_in ) if (chem_is_active()) then call t_startf('simple_chem') - call check_tracers_init(state, tracerint) - if (trim(cam_take_snapshot_before) == "chem_timestep_tend") then call cam_snapshot_all_outfld(cam_snapshot_before_num, state, tend, cam_in, cam_out, pbuf) end if @@ -998,8 +991,6 @@ subroutine tphysbc (ztodt, state, tend, pbuf, cam_out, cam_in ) call cam_snapshot_all_outfld(cam_snapshot_after_num, state, tend, cam_in, cam_out, pbuf) end if - call check_tracers_chng(state, tracerint, "chem_timestep_tend", nstep, ztodt, cam_in%cflx) - call t_stopf('simple_chem') end if