diff --git a/cime_config/testdefs/testmods_dirs/clm/irrig_o3falk_reduceOutput/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/irrig_o3falk_reduceOutput/user_nl_clm index 3ac6a15d3e..44279b6957 100644 --- a/cime_config/testdefs/testmods_dirs/clm/irrig_o3falk_reduceOutput/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/irrig_o3falk_reduceOutput/user_nl_clm @@ -2,5 +2,5 @@ ! since the o3 directory includes default, and we don't want that for this ! reducedOutput testmod o3_veg_stress_method = 'stress_falk' -hist_fincl1 += 'O3UPTAKESUN', 'O3UPTAKESHA' +hist_fincl1 += 'O3UPTAKESUN', 'O3UPTAKESHA', 'ATM_O3' hist_fincl2 += 'O3UPTAKESUN', 'O3UPTAKESHA' diff --git a/cime_config/testdefs/testmods_dirs/clm/o3lombardozzi2015/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/o3lombardozzi2015/user_nl_clm index 3a7ef0558f..c3ed58696d 100644 --- a/cime_config/testdefs/testmods_dirs/clm/o3lombardozzi2015/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/o3lombardozzi2015/user_nl_clm @@ -1,2 +1,2 @@ o3_veg_stress_method = 'stress_lombardozzi2015' - hist_fincl2 += 'O3UPTAKESUN', 'O3UPTAKESHA' + hist_fincl2 += 'O3UPTAKESUN', 'O3UPTAKESHA', 'ATM_O3' diff --git a/doc/ChangeLog b/doc/ChangeLog index eb9a26d70b..9532a92316 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,84 @@ =============================================================== +Tag name: ctsm5.1.dev108 +Originator(s): adrifoster (Adrianna Foster) +Date: Thu Sep 8 08:22:38 MDT 2022 +One-line Summary: Connect ozone from atmosphere + +Purpose and description of changes +---------------------------------- + +Allow atmospheric input of ozone partial pressure (monthly, mol/mol) to CTSM and +use this ozone in the OzoneMod module. + +Specific notes: Add ozone to atm2lndType and also add as an output history variable (ATM_O3). +Read in O3 in lnd_import_export. Connect to the OzoneMod module: +in CalcOzoneUptakeOnePoint we now use this input ozone, rather than a static parameter. + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? No + +[ ] clm5_1 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed or introduced +------------------------ + +CTSM issues fixed (include CTSM Issue #): +- Resolves ESCOMP/CTSM#270 (Receive ozone from atmosphere) + + +Notes of particular relevance for users +--------------------------------------- + +Caveats for users (e.g., need to interpolate initial conditions): Note that +currently ozone is used as an interpolated monthly variable, and is not yet +downscaled to the sub-daily scale. This will be done in a susequent PR. + +Changes to the datasets (e.g., parameter, surface or initial files): New ozone +input files used in DATM mode to drive OzoneMod + +Testing summary: +---------------- + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + All pass with only FIELDLIST diffs except for O3 tests, which have BASELINE DIFFs and NLCOMP fails. + This makes sense because we are using different ozone values to drive the OzoneMod: + + ERP_D_Ld5_P48x1.f10_f10_mg37.I2000Clm50Sp.izumi_nag.clm-o3lombardozzi2015 + ERP_P72x2_Ly3.f10_f10_mg37.I2000Clm50BgcCrop.cheyenne_intel.clm-irrig_o3falk_reduceOutput + + + cheyenne ---- OK + izumi ------- PASS + + +Answer changes +-------------- + +Changes answers relative to baseline: Yes + + Summarize any changes to answers, i.e., + - what code configurations: when o3_veg_stress_method = 'stress_falk' or 'stress_lombardozzi2015' in user_nl_clm + - what platforms/compilers: all + - nature of change: larger than roundoff + +Other details +------------- + +Pull Requests that document the changes (include PR ids): +https://github.com/ESCOMP/ctsm/pull/1837 + +=============================================================== +=============================================================== Tag name: ctsm5.1.dev107 Originator(s): adrifoster (Adrianna Foster) Date: Wed Sep 7 14:40:33 MDT 2022 diff --git a/doc/ChangeSum b/doc/ChangeSum index 635baabc4d..98a0e6f65d 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.1.dev108 afoster 09/08/2022 Connect ozone from atmosphere ctsm5.1.dev107 afoster 09/07/2022 Update Externals ctsm5.1.dev106 multiple 08/04/2022 Reuse some files generated in initialization when rerunning ctsm5.1.dev105 slevis 07/26/2022 make interfaces more similar between fsurdat_modifier and subset_data tools diff --git a/src/biogeophys/CanopyFluxesMod.F90 b/src/biogeophys/CanopyFluxesMod.F90 index 1d5298bac2..0dc9bbf797 100644 --- a/src/biogeophys/CanopyFluxesMod.F90 +++ b/src/biogeophys/CanopyFluxesMod.F90 @@ -1598,7 +1598,8 @@ subroutine CanopyFluxes(bounds, num_exposedvegp, filter_exposedvegp, rssha = photosyns_inst%rssha_patch(bounds%begp:bounds%endp), & rb = frictionvel_inst%rb1_patch(bounds%begp:bounds%endp), & ram = frictionvel_inst%ram1_patch(bounds%begp:bounds%endp), & - tlai = canopystate_inst%tlai_patch(bounds%begp:bounds%endp)) + tlai = canopystate_inst%tlai_patch(bounds%begp:bounds%endp), & + forc_o3 = atm2lnd_inst%forc_o3_grc(bounds%begg:bounds%endg)) !--------------------------------------------------------- !update Vc,max and Jmax by LUNA model diff --git a/src/biogeophys/OzoneBaseMod.F90 b/src/biogeophys/OzoneBaseMod.F90 index 36964e55c8..d9aa74e180 100644 --- a/src/biogeophys/OzoneBaseMod.F90 +++ b/src/biogeophys/OzoneBaseMod.F90 @@ -51,10 +51,10 @@ subroutine Init_interface(this, bounds, o3_veg_stress_method) character(len=*), intent(in) :: o3_veg_stress_method end subroutine Init_interface - + subroutine Restart_interface(this, bounds, ncid, flag) use decompMod , only : bounds_type - use ncdio_pio , only : file_desc_t + use ncdio_pio , only : file_desc_t import :: ozone_base_type class(ozone_base_type) :: this @@ -64,7 +64,7 @@ subroutine Restart_interface(this, bounds, ncid, flag) end subroutine Restart_interface subroutine CalcOzoneUptake_interface(this, bounds, num_exposedvegp, filter_exposedvegp, & - forc_pbot, forc_th, rssun, rssha, rb, ram, tlai) + forc_pbot, forc_th, rssun, rssha, rb, ram, tlai, forc_o3) use decompMod , only : bounds_type use shr_kind_mod , only : r8 => shr_kind_r8 import :: ozone_base_type @@ -80,6 +80,7 @@ subroutine CalcOzoneUptake_interface(this, bounds, num_exposedvegp, filter_expos real(r8) , intent(in) :: rb( bounds%begp: ) ! boundary layer resistance (s/m) real(r8) , intent(in) :: ram( bounds%begp: ) ! aerodynamical resistance (s/m) real(r8) , intent(in) :: tlai( bounds%begp: ) ! one-sided leaf area index, no burying by snow + real(r8) , intent(in) :: forc_o3( bounds%begg: ) ! atmospheric ozone (mol/mol) end subroutine CalcOzoneUptake_interface subroutine CalcOzoneStress_interface(this, bounds, & @@ -96,9 +97,9 @@ subroutine CalcOzoneStress_interface(this, bounds, & integer , intent(in) :: filter_noexposedvegp(:) ! patch filter for veg where frac_veg_nosno is 0 end subroutine CalcOzoneStress_interface end interface - + contains - + !----------------------------------------------------------------------- subroutine InitAllocateBase(this, bounds) ! @@ -114,7 +115,7 @@ subroutine InitAllocateBase(this, bounds) ! ! !LOCAL VARIABLES: integer :: begp, endp - + character(len=*), parameter :: subname = 'InitAllocateBase' !----------------------------------------------------------------------- @@ -127,7 +128,7 @@ subroutine InitAllocateBase(this, bounds) allocate(this%o3coefgsun_patch(begp:endp)) ; this%o3coefgsun_patch(:) = nan allocate(this%o3coefjmaxsha_patch(begp:endp)) ; this%o3coefjmaxsha_patch(:) = nan allocate(this%o3coefjmaxsun_patch(begp:endp)) ; this%o3coefjmaxsun_patch(:) = nan - + end subroutine InitAllocateBase @@ -148,7 +149,7 @@ subroutine InitColdBase(this, bounds) ! ! !LOCAL VARIABLES: integer :: begp, endp - + character(len=*), parameter :: subname = 'InitColdBase' !----------------------------------------------------------------------- diff --git a/src/biogeophys/OzoneMod.F90 b/src/biogeophys/OzoneMod.F90 index 892c134662..61efdfcde0 100644 --- a/src/biogeophys/OzoneMod.F90 +++ b/src/biogeophys/OzoneMod.F90 @@ -40,11 +40,11 @@ module OzoneMod ! NOTE(wjs, 2014-09-29) tlai_old_patch really belongs alongside tlai_patch in ! CanopyStateType. But there are problems with any way I can think to implement ! that: - ! + ! ! - Updating tlai_old from a call in clm_driver, just before tlai is updated: This ! is problematic to do correctly because tlai is updated in different places ! depending on whether you're using SP, CN or ED. - ! + ! ! - Updating tlai_old within each routine that updates tlai: This feels fragile, ! since it depends on each scheme remembering to do this update at the correct ! time. @@ -71,11 +71,11 @@ module OzoneMod procedure, private, nopass :: CalcOzoneUptakeOnePoint ! Original ozone stress functions from Danica Lombardozzi 2015 - procedure, private :: CalcOzoneStressLombardozzi2015 ! Stress parameterization - procedure, private, nopass :: CalcOzoneStressLombardozzi2015OnePoint ! Ozone stress calculation for single point + procedure, private :: CalcOzoneStressLombardozzi2015 ! Stress parameterization + procedure, private, nopass :: CalcOzoneStressLombardozzi2015OnePoint ! Ozone stress calculation for single point - ! Ozone stress functions from Stefanie Falk - procedure, private :: CalcOzoneStressFalk ! Stress parameterization + ! Ozone stress functions from Stefanie Falk + procedure, private :: CalcOzoneStressFalk ! Stress parameterization procedure, private, nopass :: CalcOzoneStressFalkOnePoint ! Ozone stress calculation for single point end type ozone_type @@ -84,10 +84,6 @@ module OzoneMod integer, parameter :: stress_method_lombardozzi2015 = 1 integer, parameter :: stress_method_falk = 2 - ! TODO(wjs, 2014-09-29) This parameter will eventually become a spatially-varying - ! value, obtained from ATM - real(r8), parameter :: forc_ozone = 100._r8 * 1.e-9_r8 ! ozone partial pressure [mol/mol] - ! TODO(wjs, 2014-09-29) The following parameters should eventually be moved to the ! params file. Parameters differentiated on veg type should be put on the params file ! with a pft dimension. @@ -102,9 +98,9 @@ module OzoneMod real(r8), parameter :: o3_flux_threshold = 0.8_r8 ! o3 intercepts and slopes for photosynthesis - real(r8), parameter :: needleleafPhotoInt = 0.8390_r8 ! units = unitless + real(r8), parameter :: needleleafPhotoInt = 0.8390_r8 ! units = unitless real(r8), parameter :: needleleafPhotoSlope = 0._r8 ! units = per mmol m^-2 - real(r8), parameter :: broadleafPhotoInt = 0.8752_r8 ! units = unitless + real(r8), parameter :: broadleafPhotoInt = 0.8752_r8 ! units = unitless real(r8), parameter :: broadleafPhotoSlope = 0._r8 ! units = per mmol m^-2 real(r8), parameter :: nonwoodyPhotoInt = 0.8021_r8 ! units = unitless real(r8), parameter :: nonwoodyPhotoSlope = -0.0009_r8 ! units = per mmol m^-2 @@ -116,10 +112,10 @@ module OzoneMod real(r8), parameter :: broadleafCondSlope = 0._r8 ! units = per mmol m^-2 real(r8), parameter :: nonwoodyCondInt = 0.7511_r8 ! units = unitless real(r8), parameter :: nonwoodyCondSlope = 0._r8 ! units = per mmol m^-2 - + ! Data is currently only available for broadleaf species (Dec 2020) ! o3 intercepts and slopes for JmaxO3/Jmax0 - real(r8), parameter :: needleleafJmaxInt = 1._r8 ! units = unitless + real(r8), parameter :: needleleafJmaxInt = 1._r8 ! units = unitless real(r8), parameter :: needleleafJmaxSlope = 0._r8 ! units = per mmol m^-2 real(r8), parameter :: broadleafJmaxInt = 1._r8 ! units = unitless real(r8), parameter :: broadleafJmaxSlope = -0.0037_r8 ! units = per mmol m^-2 @@ -144,19 +140,19 @@ subroutine Init(this, bounds, o3_veg_stress_method) ! ! !USES: use clm_varctl , only : use_luna - ! + ! ! !ARGUMENTS: class(ozone_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds - character(len=*), intent(in) :: o3_veg_stress_method + character(len=*), intent(in) :: o3_veg_stress_method !----------------------------------------------------------------------- - if (o3_veg_stress_method=='stress_lombardozzi2015') then + if (o3_veg_stress_method=='stress_lombardozzi2015') then this%stress_method = stress_method_lombardozzi2015 - else if (o3_veg_stress_method=='stress_falk') then + else if (o3_veg_stress_method=='stress_falk') then this%stress_method = stress_method_falk if (.not. use_luna ) call endrun(' use_luna=.true. is required when o3_veg_stress_method = stress_falk.') - else + else call endrun('unknown ozone stress method') end if @@ -280,7 +276,7 @@ subroutine InitHistory(this, bounds) call hist_addfld1d (fname='O3COEFJMAXSUN', units='unitless', & avgflag='A', long_name='ozone coefficient for maximum electron transport rate sunlit leaves', & ptr_patch=this%o3coefjmaxsun_patch, l2g_scale_type='veg') - else + else call endrun('unknown ozone stress method') end if @@ -349,7 +345,7 @@ subroutine Restart(this, bounds, ncid, flag) dim1name='pft', & long_name='ozone uptake for sunlit leaves', units='mmol m^-3', & readvar=readvar, interpinic_flag='interp', data=this%o3uptakesun_patch) - + end subroutine Restart ! ======================================================================== @@ -358,7 +354,7 @@ end subroutine Restart !----------------------------------------------------------------------- subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & - forc_pbot, forc_th, rssun, rssha, rb, ram, tlai) + forc_pbot, forc_th, rssun, rssha, rb, ram, tlai, forc_o3) ! ! !DESCRIPTION: ! Calculate ozone uptake. @@ -375,11 +371,13 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & real(r8) , intent(in) :: rb( bounds%begp: ) ! boundary layer resistance (s/m) real(r8) , intent(in) :: ram( bounds%begp: ) ! aerodynamical resistance (s/m) real(r8) , intent(in) :: tlai( bounds%begp: ) ! one-sided leaf area index, no burying by snow + real(r8) , intent(in) :: forc_o3( bounds%begg: ) ! ozone partial pressure (mol/mol) ! ! !LOCAL VARIABLES: integer :: fp ! filter index integer :: p ! patch index integer :: c ! column index + integer :: g ! gridcell index character(len=*), parameter :: subname = 'CalcOzoneUptake' !----------------------------------------------------------------------- @@ -387,6 +385,7 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & ! Enforce expected array sizes SHR_ASSERT_ALL_FL((ubound(forc_pbot) == (/bounds%endc/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(forc_th) == (/bounds%endc/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(forc_o3) == (/bounds%endg/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(rssun) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(rssha) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(rb) == (/bounds%endp/)), sourcefile, __LINE__) @@ -402,17 +401,18 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & do fp = 1, num_exposedvegp p = filter_exposedvegp(fp) c = patch%column(p) + g = patch%gridcell(p) ! Ozone uptake for shaded leaves call CalcOzoneUptakeOnePoint( & - forc_ozone=forc_ozone, forc_pbot=forc_pbot(c), forc_th=forc_th(c), & + forc_ozone=forc_o3(g), forc_pbot=forc_pbot(c), forc_th=forc_th(c), & rs=rssha(p), rb=rb(p), ram=ram(p), & tlai=tlai(p), tlai_old=tlai_old(p), pft_type=patch%itype(p), & o3uptake=o3uptakesha(p)) ! Ozone uptake for sunlit leaves call CalcOzoneUptakeOnePoint( & - forc_ozone=forc_ozone, forc_pbot=forc_pbot(c), forc_th=forc_th(c), & + forc_ozone=forc_o3(g), forc_pbot=forc_pbot(c), forc_th=forc_th(c), & rs=rssun(p), rb=rb(p), ram=ram(p), & tlai=tlai(p), tlai_old=tlai_old(p), pft_type=patch%itype(p), & o3uptake=o3uptakesun(p)) @@ -670,7 +670,7 @@ subroutine CalcOzoneStressLombardozzi2015OnePoint( & end subroutine CalcOzoneStressLombardozzi2015OnePoint - + !----------------------------------------------------------------------- subroutine CalcOzoneStressFalk(this, bounds, & num_exposedvegp, filter_exposedvegp, & @@ -683,7 +683,7 @@ subroutine CalcOzoneStressFalk(this, bounds, & ! ! !USES: use LunaMod , only : is_time_to_run_LUNA - ! + ! ! !ARGUMENTS: class(ozone_type), intent(inout) :: this type(bounds_type), intent(in) :: bounds @@ -698,16 +698,16 @@ subroutine CalcOzoneStressFalk(this, bounds, & character(len=*), parameter :: subname = 'CalcOzoneStressFalk' !----------------------------------------------------------------------- - - if (is_time_to_run_LUNA()) then - + + if (is_time_to_run_LUNA()) then + associate( & o3uptakesha => this%o3uptakesha_patch , & ! Input: [real(r8) (:)] ozone dose o3uptakesun => this%o3uptakesun_patch , & ! Input: [real(r8) (:)] ozone dose o3coefjmaxsha => this%o3coefjmaxsha_patch , & ! Output: [real(r8) (:)] ozone coef jmax sha o3coefjmaxsun => this%o3coefjmaxsun_patch & ! Output: [real(r8) (:)] ozone coef jmax sun ) - + do fp = 1, num_exposedvegp p = filter_exposedvegp(fp) @@ -734,7 +734,7 @@ subroutine CalcOzoneStressFalk(this, bounds, & end associate - end if + end if end subroutine CalcOzoneStressFalk @@ -750,13 +750,13 @@ subroutine CalcOzoneStressFalkOnePoint( pft_type, o3uptake, o3coefjmax) integer , intent(in) :: pft_type ! vegetation type, for indexing into pftvarcon arrays real(r8) , intent(in) :: o3uptake ! ozone entering the leaf real(r8) , intent(inout) :: o3coefjmax ! ozone coefficient for max. electron transport rate - ! + ! ! !LOCAL VARIABLES: real(r8) :: jmaxInt ! intercept for max. electron transport rate real(r8) :: jmaxSlope ! slope for max. electron transport rate character(len=*), parameter :: subname = 'CalcOzoneStressFalkOnePoint' !----------------------------------------------------------------------- - + if (o3uptake == 0._r8) then o3coefjmax = 1._r8 else @@ -776,7 +776,7 @@ subroutine CalcOzoneStressFalkOnePoint( pft_type, o3uptake, o3coefjmax) jmaxSlope = needleleafJmaxSlope end if ! Apply parameter values to compute o3 coefficients - o3coefjmax = max(0._r8, min(1._r8, jmaxInt + jmaxSlope * o3uptake)) + o3coefjmax = max(0._r8, min(1._r8, jmaxInt + jmaxSlope * o3uptake)) end if end subroutine CalcOzoneStressFalkOnePoint diff --git a/src/biogeophys/OzoneOffMod.F90 b/src/biogeophys/OzoneOffMod.F90 index b2b7f8863a..b22f789147 100644 --- a/src/biogeophys/OzoneOffMod.F90 +++ b/src/biogeophys/OzoneOffMod.F90 @@ -34,7 +34,7 @@ module OzoneOffMod __FILE__ contains - + !----------------------------------------------------------------------- function constructor() result(ozone_off) ! @@ -47,7 +47,7 @@ function constructor() result(ozone_off) type(ozone_off_type) :: ozone_off ! function result ! ! !LOCAL VARIABLES: - + character(len=*), parameter :: subname = 'constructor' !----------------------------------------------------------------------- @@ -56,7 +56,7 @@ function constructor() result(ozone_off) ! Eventually this should call the Init routine (or replace the Init routine ! entirely). But I think it would be confusing to do that until we switch everything ! to use a constructor rather than the init routine. - + end function constructor @@ -71,30 +71,30 @@ subroutine Init(this, bounds, o3_veg_stress_method) ! !ARGUMENTS: class(ozone_off_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds - character(len=*), intent(in) :: o3_veg_stress_method + character(len=*), intent(in) :: o3_veg_stress_method !----------------------------------------------------------------------- if (o3_veg_stress_method /= 'unset' ) call endrun(' unconsistent choice of o3_veg_stress_method in init OzoneOffMod.') - + call this%InitAllocateBase(bounds) call this%InitColdBase(bounds) end subroutine Init subroutine Restart(this, bounds, ncid, flag) - use ncdio_pio , only : file_desc_t + use ncdio_pio , only : file_desc_t class(ozone_off_type) :: this type(bounds_type), intent(in) :: bounds type(file_desc_t) , intent(inout) :: ncid ! netcdf id character(len=*) , intent(in) :: flag ! 'read', 'write' or 'define' - + ! DO NOTHING end subroutine Restart subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & - forc_pbot, forc_th, rssun, rssha, rb, ram, tlai) + forc_pbot, forc_th, rssun, rssha, rb, ram, tlai, forc_o3) class(ozone_off_type) , intent(inout) :: this type(bounds_type) , intent(in) :: bounds @@ -107,6 +107,7 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & real(r8) , intent(in) :: rb( bounds%begp: ) ! boundary layer resistance (s/m) real(r8) , intent(in) :: ram( bounds%begp: ) ! aerodynamical resistance (s/m) real(r8) , intent(in) :: tlai( bounds%begp: ) ! one-sided leaf area index, no burying by snow + real(r8) , intent(in) :: forc_o3( bounds%begg: ) ! atmospheric ozone (mol/mol) ! Enforce expected array sizes (mainly so that a debug-mode threaded test with ! ozone-off can pick up problems with the call to this routine) @@ -117,6 +118,7 @@ subroutine CalcOzoneUptake(this, bounds, num_exposedvegp, filter_exposedvegp, & SHR_ASSERT_ALL_FL((ubound(rb) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(ram) == (/bounds%endp/)), sourcefile, __LINE__) SHR_ASSERT_ALL_FL((ubound(tlai) == (/bounds%endp/)), sourcefile, __LINE__) + SHR_ASSERT_ALL_FL((ubound(forc_o3) == (/bounds%endg/)), sourcefile, __LINE__) ! Do nothing: In the ozone off case, we don't need to track ozone uptake diff --git a/src/cpl/nuopc/lnd_import_export.F90 b/src/cpl/nuopc/lnd_import_export.F90 index 57d4db7145..0e7a5e2eef 100644 --- a/src/cpl/nuopc/lnd_import_export.F90 +++ b/src/cpl/nuopc/lnd_import_export.F90 @@ -93,6 +93,7 @@ module lnd_import_export character(*), parameter :: Faxa_dstdry = 'Faxa_dstdry' character(*), parameter :: Sa_methane = 'Sa_methaneaxa_ndep' character(*), parameter :: Faxa_ndep = 'Faxa_ndep' + character(*), parameter :: Sa_o3 = 'Sa_o3' character(*), parameter :: Sa_co2prog = 'Sa_co2prog' character(*), parameter :: Sa_co2diag = 'Sa_co2diag' character(*), parameter :: Flrr_flood = 'Flrr_flood' @@ -350,6 +351,7 @@ subroutine advertise_fields(gcomp, flds_scalar_name, glc_present, cism_evolve, r call fldlist_add(fldsToLnd_num, fldsToLnd, Faxa_swvdr ) call fldlist_add(fldsToLnd_num, fldsToLnd, Faxa_swndf ) call fldlist_add(fldsToLnd_num, fldsToLnd, Faxa_swvdf ) + call fldlist_add(fldsToLnd_num, fldsToLnd, Sa_o3 ) ! from atm - black carbon deposition fluxes (3) ! (1) => bcphidry, (2) => bcphodry, (3) => bcphiwet @@ -534,6 +536,8 @@ subroutine import_fields( gcomp, bounds, glc_present, rof_prognostic, & if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport_1d(importState, Sa_tbot , atm2lnd_inst%forc_t_not_downscaled_grc(begg:), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return + call state_getimport_1d(importState, Sa_o3 , atm2lnd_inst%forc_o3_grc(begg:), rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport_1d(importState, Faxa_rainc, forc_rainc(begg:), rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return call state_getimport_1d(importState, Faxa_rainl, forc_rainl(begg:), rc=rc) diff --git a/src/main/atm2lndType.F90 b/src/main/atm2lndType.F90 index a2d09ca85c..53013caf24 100644 --- a/src/main/atm2lndType.F90 +++ b/src/main/atm2lndType.F90 @@ -89,13 +89,14 @@ module atm2lndType real(r8), pointer :: forc_po2_240_patch (:) => null() ! 10-day mean O2 partial pressure (Pa) real(r8), pointer :: forc_aer_grc (:,:) => null() ! aerosol deposition array real(r8), pointer :: forc_pch4_grc (:) => null() ! CH4 partial pressure (Pa) + real(r8), pointer :: forc_o3_grc (:) => null() ! ozone partial pressure (mol/mol) - real(r8), pointer :: forc_t_not_downscaled_grc (:) => null() ! not downscaled atm temperature (Kelvin) - real(r8), pointer :: forc_th_not_downscaled_grc (:) => null() ! not downscaled atm potential temperature (Kelvin) - real(r8), pointer :: forc_pbot_not_downscaled_grc (:) => null() ! not downscaled atm pressure (Pa) - real(r8), pointer :: forc_pbot240_downscaled_patch (:) => null() ! 10-day mean downscaled atm pressure (Pa) - real(r8), pointer :: forc_rho_not_downscaled_grc (:) => null() ! not downscaled atm density (kg/m**3) - real(r8), pointer :: forc_lwrad_not_downscaled_grc (:) => null() ! not downscaled atm downwrd IR longwave radiation (W/m**2) + real(r8), pointer :: forc_t_not_downscaled_grc (:) => null() ! not downscaled atm temperature (Kelvin) + real(r8), pointer :: forc_th_not_downscaled_grc (:) => null() ! not downscaled atm potential temperature (Kelvin) + real(r8), pointer :: forc_pbot_not_downscaled_grc (:) => null() ! not downscaled atm pressure (Pa) + real(r8), pointer :: forc_pbot240_downscaled_patch (:) => null() ! 10-day mean downscaled atm pressure (Pa) + real(r8), pointer :: forc_rho_not_downscaled_grc (:) => null() ! not downscaled atm density (kg/m**3) + real(r8), pointer :: forc_lwrad_not_downscaled_grc (:) => null() ! not downscaled atm downwrd IR longwave radiation (W/m**2) ! atm->lnd downscaled real(r8), pointer :: forc_t_downscaled_col (:) => null() ! downscaled atm temperature (Kelvin) @@ -106,10 +107,10 @@ module atm2lndType ! time averaged quantities - real(r8) , pointer :: fsd24_patch (:) => null() ! patch 24hr average of direct beam radiation - real(r8) , pointer :: fsd240_patch (:) => null() ! patch 240hr average of direct beam radiation - real(r8) , pointer :: fsi24_patch (:) => null() ! patch 24hr average of diffuse beam radiation - real(r8) , pointer :: fsi240_patch (:) => null() ! patch 240hr average of diffuse beam radiation + real(r8) , pointer :: fsd24_patch (:) => null() ! patch 24hr average of direct beam radiation + real(r8) , pointer :: fsd240_patch (:) => null() ! patch 240hr average of direct beam radiation + real(r8) , pointer :: fsi24_patch (:) => null() ! patch 24hr average of diffuse beam radiation + real(r8) , pointer :: fsi240_patch (:) => null() ! patch 240hr average of diffuse beam radiation real(r8) , pointer :: wind24_patch (:) => null() ! patch 24-hour running mean of wind real(r8) , pointer :: t_mo_patch (:) => null() ! patch 30-day average temperature (Kelvin) real(r8) , pointer :: t_mo_min_patch (:) => null() ! patch annual min of t_mo (Kelvin) @@ -120,7 +121,7 @@ module atm2lndType procedure, public :: InitForTesting ! version of Init meant for unit testing procedure, private :: ReadNamelist procedure, private :: InitAllocate - procedure, private :: InitHistory + procedure, private :: InitHistory procedure, public :: InitAccBuffer procedure, public :: InitAccVars procedure, public :: UpdateAccVars @@ -289,7 +290,7 @@ subroutine Init(this, bounds, NLFilename) call this%InitAllocate(bounds) call this%ReadNamelist(NLFilename) call this%InitHistory(bounds) - + end subroutine Init !----------------------------------------------------------------------- @@ -450,7 +451,7 @@ subroutine InitAllocate(this, bounds) ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: real(r8) :: ival = 0.0_r8 ! initial value @@ -482,6 +483,7 @@ subroutine InitAllocate(this, bounds) allocate(this%forc_po2_grc (begg:endg)) ; this%forc_po2_grc (:) = ival allocate(this%forc_aer_grc (begg:endg,14)) ; this%forc_aer_grc (:,:) = ival allocate(this%forc_pch4_grc (begg:endg)) ; this%forc_pch4_grc (:) = ival + allocate(this%forc_o3_grc (begg:endg)) ; this%forc_o3_grc (:) = ival if(use_luna)then allocate(this%forc_pco2_240_patch (begp:endp)) ; this%forc_pco2_240_patch (:) = ival allocate(this%forc_po2_240_patch (begp:endp)) ; this%forc_po2_240_patch (:) = ival @@ -494,7 +496,7 @@ subroutine InitAllocate(this, bounds) allocate(this%forc_th_not_downscaled_grc (begg:endg)) ; this%forc_th_not_downscaled_grc (:) = ival allocate(this%forc_rho_not_downscaled_grc (begg:endg)) ; this%forc_rho_not_downscaled_grc (:) = ival allocate(this%forc_lwrad_not_downscaled_grc (begg:endg)) ; this%forc_lwrad_not_downscaled_grc (:) = ival - + ! atm->lnd downscaled allocate(this%forc_t_downscaled_col (begc:endc)) ; this%forc_t_downscaled_col (:) = ival allocate(this%forc_pbot_downscaled_col (begc:endc)) ; this%forc_pbot_downscaled_col (:) = ival @@ -522,7 +524,7 @@ subroutine InitHistory(this, bounds) ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: begg, endg @@ -553,6 +555,10 @@ subroutine InitHistory(this, bounds) avgflag='A', long_name='atmospheric surface height', & ptr_lnd=this%forc_topo_grc) + call hist_addfld1d (fname='ATM_O3', units='mol/mol', & + avgflag='A', long_name='atmospheric ozone partial pressure', & + ptr_lnd=this%forc_o3_grc, default = 'inactive') + this%forc_solar_grc(begg:endg) = spval call hist_addfld1d (fname='FSDS', units='W/m^2', & avgflag='A', long_name='atmospheric incident solar radiation', & @@ -668,13 +674,13 @@ subroutine InitAccBuffer (this, bounds) ! This routine set defaults values that are then overwritten by the ! restart file for restart or branch runs ! - ! !USES + ! !USES use clm_varcon , only : spval use accumulMod , only : init_accum_field ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds !--------------------------------------------------------------------- this%fsd24_patch(bounds%begp:bounds%endp) = spval @@ -729,16 +735,16 @@ subroutine InitAccVars(this, bounds) ! !DESCRIPTION: ! Initialize module variables that are associated with ! time accumulated fields. This routine is called for both an initial run - ! and a restart run (and must therefore must be called after the restart file + ! and a restart run (and must therefore must be called after the restart file ! is read in and the accumulation buffer is obtained) ! - ! !USES + ! !USES use accumulMod , only : extract_accum_field use clm_time_manager , only : get_nstep ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type), intent(in) :: bounds + type(bounds_type), intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: begp, endp @@ -783,7 +789,7 @@ subroutine InitAccVars(this, bounds) this%fsi240_patch(begp:endp) = rbufslp(begp:endp) if (use_cndv) then - call extract_accum_field ('TDA', rbufslp, nstep) + call extract_accum_field ('TDA', rbufslp, nstep) this%t_mo_patch(begp:endp) = rbufslp(begp:endp) end if @@ -797,11 +803,11 @@ subroutine InitAccVars(this, bounds) this%forc_po2_240_patch(begp:endp) = rbufslp(begp:endp) call extract_accum_field ('pco2_240', rbufslp, nstep) - this%forc_pco2_240_patch(begp:endp) = rbufslp(begp:endp) + this%forc_pco2_240_patch(begp:endp) = rbufslp(begp:endp) call extract_accum_field ('pbot240', rbufslp, nstep) - this%forc_pbot240_downscaled_patch(begp:endp) = rbufslp(begp:endp) - + this%forc_pbot240_downscaled_patch(begp:endp) = rbufslp(begp:endp) + endif deallocate(rbufslp) @@ -818,7 +824,7 @@ subroutine UpdateAccVars (this, bounds) ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type) , intent(in) :: bounds + type(bounds_type) , intent(in) :: bounds ! ! !LOCAL VARIABLES: integer :: g,c,p ! indices @@ -849,7 +855,7 @@ subroutine UpdateAccVars (this, bounds) call endrun(msg=errMsg(sourcefile, __LINE__)) endif - ! Accumulate and extract forc_solad24 & forc_solad240 + ! Accumulate and extract forc_solad24 & forc_solad240 do p = begp,endp g = patch%gridcell(p) rbufslp(p) = this%forc_solad_grc(g,1) @@ -859,7 +865,7 @@ subroutine UpdateAccVars (this, bounds) call update_accum_field ('FSD24' , rbufslp , nstep) call extract_accum_field ('FSD24' , this%fsd24_patch , nstep) - ! Accumulate and extract forc_solai24 & forc_solai240 + ! Accumulate and extract forc_solai24 & forc_solai240 do p = begp,endp g = patch%gridcell(p) rbufslp(p) = this%forc_solai_grc(g,1) @@ -872,9 +878,9 @@ subroutine UpdateAccVars (this, bounds) if (use_cndv) then - ! Accumulate and extract TDA (accumulates TBOT as 30-day average) and + ! Accumulate and extract TDA (accumulates TBOT as 30-day average) and ! also determines t_mo_min - + do p = begp,endp c = patch%column(p) rbufslp(p) = this%forc_t_downscaled_col(c) @@ -890,8 +896,8 @@ subroutine UpdateAccVars (this, bounds) if (use_fates) then do p = bounds%begp,bounds%endp - g = patch%gridcell(p) - rbufslp(p) = this%forc_wind_grc(g) + g = patch%gridcell(p) + rbufslp(p) = this%forc_wind_grc(g) end do call update_accum_field ('WIND24', rbufslp, nstep) call extract_accum_field ('WIND24', this%wind24_patch, nstep) @@ -901,21 +907,21 @@ subroutine UpdateAccVars (this, bounds) if(use_luna) then do p = bounds%begp,bounds%endp g = patch%gridcell(p) - rbufslp(p) = this%forc_pco2_grc(g) + rbufslp(p) = this%forc_pco2_grc(g) enddo call update_accum_field ('pco2_240', rbufslp, nstep) call extract_accum_field ('pco2_240', this%forc_pco2_240_patch, nstep) do p = bounds%begp,bounds%endp g = patch%gridcell(p) - rbufslp(p) = this%forc_po2_grc(g) + rbufslp(p) = this%forc_po2_grc(g) enddo call update_accum_field ('po2_240', rbufslp, nstep) call extract_accum_field ('po2_240', this%forc_po2_240_patch, nstep) do p = bounds%begp,bounds%endp c = patch%column(p) - rbufslp(p) = this%forc_pbot_downscaled_col(c) + rbufslp(p) = this%forc_pbot_downscaled_col(c) enddo call update_accum_field ('pbot240', rbufslp, nstep) call extract_accum_field ('pbot240', this%forc_pbot240_downscaled_patch, nstep) @@ -929,19 +935,19 @@ end subroutine UpdateAccVars !------------------------------------------------------------------------ subroutine Restart(this, bounds, ncid, flag) - ! + ! ! !USES: use restUtilMod use ncdio_pio ! ! !ARGUMENTS: class(atm2lnd_type) :: this - type(bounds_type), intent(in) :: bounds - type(file_desc_t), intent(inout) :: ncid - character(len=*) , intent(in) :: flag + type(bounds_type), intent(in) :: bounds + type(file_desc_t), intent(inout) :: ncid + character(len=*) , intent(in) :: flag ! ! !LOCAL VARIABLES: - logical :: readvar + logical :: readvar !------------------------------------------------------------------------ if (use_cndv) then @@ -999,6 +1005,7 @@ subroutine Clean(this) deallocate(this%forc_po2_grc) deallocate(this%forc_aer_grc) deallocate(this%forc_pch4_grc) + deallocate(this%forc_o3_grc) ! atm->lnd not downscaled deallocate(this%forc_t_not_downscaled_grc) @@ -1006,7 +1013,7 @@ subroutine Clean(this) deallocate(this%forc_th_not_downscaled_grc) deallocate(this%forc_rho_not_downscaled_grc) deallocate(this%forc_lwrad_not_downscaled_grc) - + ! atm->lnd downscaled deallocate(this%forc_t_downscaled_col) deallocate(this%forc_pbot_downscaled_col)