diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm index 07e78952f9..ab0aab0cc5 100755 --- a/bld/CLMBuildNamelist.pm +++ b/bld/CLMBuildNamelist.pm @@ -3395,7 +3395,7 @@ sub setup_logic_mineral_nitrogen_dynamics { # my ($opts, $nl_flags, $definition, $defaults, $nl) = @_; - my @vars = ( "freelivfix_slope_wet", "freelivfix_intercept" ); + my @vars = ( "freelivfix_slope_wet", "freelivfix_intercept", "nfix_method" ); if ( &value_is_true($nl_flags->{'use_cn'}) && &value_is_true($nl->get_value('use_fun')) ) { foreach my $var ( @vars ) { add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var, @@ -3408,6 +3408,7 @@ sub setup_logic_mineral_nitrogen_dynamics { } } } + } @@ -5208,6 +5209,7 @@ sub write_output_files { } push @groups, "clm_humanindex_inparm"; push @groups, "cnmresp_inparm"; + push @groups, "cnfun_inparm"; push @groups, "photosyns_inparm"; push @groups, "cnfire_inparm"; push @groups, "cn_general"; diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml index 7c75b471a6..cee72ca5cf 100644 --- a/bld/namelist_files/namelist_defaults_ctsm.xml +++ b/bld/namelist_files/namelist_defaults_ctsm.xml @@ -524,10 +524,9 @@ attributes from the config_cache.xml file (with keys converted to upper-case). -lnd/clm2/paramdata/ctsm60_params.c241017.nc -lnd/clm2/paramdata/ctsm51_params.c241017.nc -lnd/clm2/paramdata/clm50_params.c241017.nc -lnd/clm2/paramdata/clm45_params.c241017.nc +lnd/clm2/paramdata/ctsm60_params.c241119.nc +lnd/clm2/paramdata/clm50_params.c241119.nc +lnd/clm2/paramdata/clm45_params.c241119.nc @@ -1894,6 +1893,8 @@ lnd/clm2/surfdata_esmf/NEON/ctsm5.3.0/surfdata_1x1_NEON_TOOL_hist_2000_78pfts_c2 0.0117d00 0.0006d00 +Houlton + 0.83d-06 diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml index 2d5ab0e4c7..ac61b86852 100644 --- a/bld/namelist_files/namelist_definition_ctsm.xml +++ b/bld/namelist_files/namelist_definition_ctsm.xml @@ -388,6 +388,11 @@ Slope of free living Nitrogen fixation with annual ET Intercept of free living Nitrogen fixation with zero annual ET + +Choice of nfix parameterization + + If TRUE use the undercanopy stability term used with CLM4.5 (Sakaguchi&Zeng, 2008) diff --git a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm index edeb0fce21..8a1e5bb216 100644 --- a/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/ciso_cwd_hr/user_nl_clm @@ -1,2 +1,2 @@ -paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm51_ciso_cwd_hr_params.c241017.nc' +paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm60_ciso_cwd_hr_params.c241119.nc' hist_fincl1 = 'CWDC_HR','C13_CWDC_HR','C14_CWDC_HR','CWD_HR_L2','CWD_HR_L2_vr','CWD_HR_L3','CWD_HR_L3_vr' diff --git a/cime_config/testdefs/testmods_dirs/clm/clm60_monthly_matrixcn_soilCN30/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/clm60_monthly_matrixcn_soilCN30/user_nl_clm index b1d856797d..d982aea0f0 100644 --- a/cime_config/testdefs/testmods_dirs/clm/clm60_monthly_matrixcn_soilCN30/user_nl_clm +++ b/cime_config/testdefs/testmods_dirs/clm/clm60_monthly_matrixcn_soilCN30/user_nl_clm @@ -1,2 +1,2 @@ use_soil_matrixcn = .true. -paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm60_params_cn30.c241017.nc' +paramfile = '$DIN_LOC_ROOT/lnd/clm2/paramdata/ctsm60_params_cn30.c241119.nc' diff --git a/doc/ChangeLog b/doc/ChangeLog index cc415555ba..ed6892a285 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,88 @@ =============================================================== +Tag name: ctsm5.3.017 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Tue 24 Dec 2024 03:43:01 PM MST +One-line Summary: Merge b4b-dev + +Purpose and description of changes +---------------------------------- + + #2869 Update temperature cost function for symbiotic Nfix in FUN + + +Significant changes to scientifically-supported configurations +-------------------------------------------------------------- + +Does this tag change answers significantly for any of the following physics configurations? +(Details of any changes will be given in the "Answer changes" section below.) + + [Put an [X] in the box for any configuration with significant answer changes.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + Resolves #2869 + +Notes of particular relevance for users +--------------------------------------- +Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables): + New namelist variable: nfix_method + +Changes made to namelist defaults (e.g., changed parameter values): + nfix_method default: Houlton + other available option: Bytnerowicz + + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - PASS + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- machine seems problematic at the moment + + +Answer changes +-------------- + +Changes answers relative to baseline: No, but read caveat. + + Summarize any changes to answers, i.e., + - what code configurations: tests with ciso_cwd_hr testmods + - what platforms/compilers: all + - nature of change: irrelevant + + Explanation: + "diff ctsm60_ciso_cwd_hr_params.c241119.asc ctsm60_params.c241119.asc" + differ ONLY in the value of ceta. The previous paramfile for ciso_cwd_hr + showed additional diffs, likely due to problems that we have seen before + when generating new paramfiles. The ciso_cwd_hr tests would not have shown + diffs had the previous paramfile been correct, so I only mention the diffs + here for the record. + + +Other details +------------- +Pull Requests that document the changes (include PR ids): + https://github.com/ESCOMP/ctsm/pull/2869 + +=============================================================== +=============================================================== Tag name: ctsm5.3.016 Originator(s): jedwards and erik (Erik Kluzek,UCAR/TSS,303-497-1326) Date: Thu 19 Dec 2024 04:23:39 PM MST diff --git a/doc/ChangeSum b/doc/ChangeSum index 2c0cec5d21..60d46392ff 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.3.017 slevis 12/24/2024 Merge b4b-dev ctsm5.3.016 erik 12/19/2024 Rpointer files for restart now have the simulation date in the filename ctsm5.3.015 erik 12/18/2024 Update cdeps with cam7 nextsw cday changes ctsm5.3.014 erik 12/03/2024 Bring in several fixes for testing in the previous cesm3_0_beta03/04 tags diff --git a/src/biogeochem/CNFUNMod.F90 b/src/biogeochem/CNFUNMod.F90 index a6614fe4b9..0af5efe580 100644 --- a/src/biogeochem/CNFUNMod.F90 +++ b/src/biogeochem/CNFUNMod.F90 @@ -26,7 +26,7 @@ module CNFUNMod use pftconMod , only : pftcon, npcropmin use decompMod , only : bounds_type use clm_varctl , only : use_nitrif_denitrif,use_flexiblecn - use CNSharedParamsMod , only : use_matrixcn + use CNSharedParamsMod , only : use_matrixcn use abortutils , only : endrun use CNVegstateType , only : cnveg_state_type use CNVegCarbonStateType , only : cnveg_carbonstate_type @@ -48,12 +48,18 @@ module CNFUNMod private ! ! !PUBLIC MEMBER FUNCTIONS: + public :: CNFUNReadNML ! Read in namelist variables public:: readParams ! Read in parameters needed for FUN public:: CNFUNInit ! FUN calculation initialization public:: CNFUN ! Run FUN + character(len=25) :: nfix_method ! choice of nfix parameterization + type, private :: params_type real(r8) :: ndays_off ! number of days to complete leaf offset + real(r8), allocatable :: nfix_tmin(:) ! A BNF parameter + real(r8), allocatable :: nfix_topt(:) ! A BNF parameter + real(r8), allocatable :: nfix_tmax(:) ! A BNF parameter end type params_type ! @@ -82,17 +88,73 @@ module CNFUNMod contains !-------------------------------------------------------------------- !--- + subroutine CNFUNReadNML(NLFilename) + ! + ! !DESCRIPTION: + ! Read in namelist variables + ! + ! !USES: + use fileutils , only : getavu, relavu, opnfil + use shr_nl_mod , only : shr_nl_find_group_name + use spmdMod , only : masterproc, mpicom + use shr_mpi_mod, only : shr_mpi_bcast + use clm_varctl , only : iulog + use spmdMod , only : MPI_CHARACTER + ! + ! !ARGUMENTS: + character(len=*), intent(in) :: NLFilename ! Namelist filename + ! + ! !LOCAL VARIABLES: + integer :: ierr ! error code + integer :: unitn ! unit for namelist file + + character(len=*), parameter :: nmlname = 'cnfun_inparm' + !----------------------------------------------------------------------- + + namelist /cnfun_inparm/ nfix_method + + ! Initialize options to default values, in case they are not specified in + ! the namelist + + if (masterproc) then + unitn = getavu() + write(iulog,*) 'Read in '//nmlname//' namelist' + call opnfil (NLFilename, unitn, 'F') + call shr_nl_find_group_name(unitn, nmlname, status=ierr) + if (ierr == 0) then + read(unitn, nml=cnfun_inparm, iostat=ierr) + if (ierr /= 0) then + call endrun(msg="ERROR reading "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + else + call endrun(msg="ERROR finding "//nmlname//"namelist"//errmsg(sourcefile, __LINE__)) + end if + call relavu( unitn ) + end if + + call mpi_bcast (nfix_method, len(nfix_method), MPI_CHARACTER, 0, mpicom, ierr) + + if (masterproc) then + write(iulog,*) ' ' + write(iulog,*) nmlname//' settings:' + write(iulog,nml=cnfun_inparm) + write(iulog,*) ' ' + end if + + end subroutine CNFUNReadNML + + !----------------------------------------------------------------------- subroutine readParams ( ncid ) ! ! !USES: use ncdio_pio , only : file_desc_t,ncd_io + use clm_varpar, only : mxpft ! !ARGUMENTS: implicit none type(file_desc_t),intent(inout) :: ncid ! pio netCDF file id ! ! !LOCAL VARIABLES: - character(len=32) :: subname = 'CNFUNParamsType' character(len=100) :: errCode = '-Error reading in parameters file:' logical :: readv ! has variable been read in or not real(r8) :: tempr ! temporary to read in parameter @@ -107,6 +169,20 @@ subroutine readParams ( ncid ) if ( .not. readv ) call endrun( msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) params_inst%ndays_off=tempr + allocate(params_inst%nfix_tmin(0:mxpft)) + tString='nfix_tmin' + call ncd_io(trim(tString), params_inst%nfix_tmin(:), 'read', ncid, readvar=readv) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + allocate(params_inst%nfix_topt(0:mxpft)) + tString='nfix_topt' + call ncd_io(trim(tString), params_inst%nfix_topt(:), 'read', ncid, readvar=readv) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) + + allocate(params_inst%nfix_tmax(0:mxpft)) + tString='nfix_tmax' + call ncd_io(trim(tString), params_inst%nfix_tmax(:), 'read', ncid, readvar=readv) + if ( .not. readv ) call endrun(msg=trim(errCode)//trim(tString)//errMsg(sourcefile, __LINE__)) end subroutine readParams @@ -135,7 +211,6 @@ subroutine CNFUNInit (bounds,cnveg_state_inst,cnveg_carbonstate_inst,cnveg_nitro integer :: nstep ! time step number integer :: nstep_fun ! Number of ! atmospheric timesteps between calls to FUN - character(len=32) :: subname = 'CNFUNInit' !-------------------------------------------------------------------- !--- @@ -290,7 +365,7 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& real(r8) :: litterfall_n(bounds%begp:bounds%endp) ! N loss based on the leafc to litter (gN/m2) real(r8) :: litterfall_n_step(bounds%begp:bounds%endp,1:nstp) ! N loss based on the leafc to litter (gN/m2) real(r8) :: litterfall_c_step(bounds%begp:bounds%endp,1:nstp) ! N loss based on the leafc to litter (gN/m2) - real(r8) :: tc_soisno(bounds%begc:bounds%endc,1:nlevdecomp) ! Soil temperature (degrees Celsius) + real(r8) :: tc_soisno(bounds%begc:bounds%endc,1:nlevdecomp) ! Soil temperature (degrees Celsius) real(r8) :: npp_remaining(bounds%begp:bounds%endp,1:nstp) ! A temporary variable for npp_remaining(gC/m2) real(r8) :: n_passive_step(bounds%begp:bounds%endp,1:nstp) ! N taken up by transpiration at substep(gN/m2) real(r8) :: n_passive_acc(bounds%begp:bounds%endp) ! N acquired by passive uptake (gN/m2) @@ -460,14 +535,13 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& real(r8) :: total_N_resistance ! C to of N for whole soil -leaf ! pathway real(r8) :: free_RT_frac=0.0_r8 !fraction of N retranslocation which is automatic/free. - ! SHould be made into a PFT parameter. + ! Should be made into a PFT parameter. real(r8) :: paid_for_n_retrans real(r8) :: free_n_retrans real(r8) :: total_c_spent_retrans real(r8) :: total_c_accounted_retrans - !------end of not_use_nitrif_denitrif------! !-------------------------------------------------------------------- !------------ @@ -495,9 +569,10 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& ! fixers, 2 for non fixers. This will become redundant with the ! 'fixer' parameter if it works. + character(len=100) :: errCode !-------------------------------------------------------------------- !--------------------------------- - associate(ivt => patch%itype , & ! Input: [integer (:) ] p + associate(ivt => patch%itype , & ! Input: [integer (:) ] p leafcn => pftcon%leafcn , & ! Input: leaf C:N (gC/gN) season_decid => pftcon%season_decid , & ! Input: binary flag for seasonal ! -deciduous leaf habit (0 or 1) @@ -522,10 +597,10 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& perecm => pftcon%perecm , & ! Input: The fraction of ECM ! -associated PFT grperc => pftcon%grperc , & ! Input: growth percentage - fun_cn_flex_a => pftcon%fun_cn_flex_a , & ! Parameter a of FUN-flexcn link code (def 5) - fun_cn_flex_b => pftcon%fun_cn_flex_b , & ! Parameter b of FUN-flexcn link code (def 200) - fun_cn_flex_c => pftcon%fun_cn_flex_c , & ! Parameter b of FUN-flexcn link code (def 80) - FUN_fracfixers => pftcon%FUN_fracfixers , & ! Fraction of C that can be used for fixation. + fun_cn_flex_a => pftcon%fun_cn_flex_a , & ! Parameter a of FUN-flexcn link code (def 5) + fun_cn_flex_b => pftcon%fun_cn_flex_b , & ! Parameter b of FUN-flexcn link code (def 200) + fun_cn_flex_c => pftcon%fun_cn_flex_c , & ! Parameter b of FUN-flexcn link code (def 80) + FUN_fracfixers => pftcon%FUN_fracfixers , & ! Fraction of C that can be used for fixation. leafcn_offset => cnveg_state_inst%leafcn_offset_patch , & ! Output: ! [real(r8) (:)] Leaf C:N used by FUN plantCN => cnveg_state_inst%plantCN_patch , & ! Output: [real(r8) (:)] Plant @@ -1041,9 +1116,7 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& npp_to_nonmyc_nh4(:) = 0.0_r8 npp_to_fixation(:) = 0.0_r8 npp_to_retrans(:) = 0.0_r8 - - - + unmetDemand = .TRUE. plant_ndemand_pool_step(p,istp) = plant_ndemand_pool(p) * permyc(p,istp) npp_remaining(p,istp) = availc_pool(p) * permyc(p,istp) @@ -1051,32 +1124,42 @@ subroutine CNFUN(bounds,num_soilc, filter_soilc,num_soilp& ! if (plant_ndemand_pool_step(p,istp) .gt. 0._r8) then ! ! plant_ndemand_pool_step > 0.0 - + do j = 1, nlevdecomp tc_soisno(c,j) = t_soisno(c,j) - tfrz + if(pftcon%c3psn(patch%itype(p)).eq.1)then fixer=1 else fixer=0 endif - costNit(j,icostFix) = fun_cost_fix(fixer,a_fix(ivt(p)),b_fix(ivt(p))& - ,c_fix(ivt(p)) ,big_cost,crootfr(p,j),s_fix(ivt(p)),tc_soisno(c,j)) + + select case (nfix_method) + case ('Houlton') + costNit(j,icostFix) = fun_cost_fix(fixer, & + a_fix(ivt(p)), b_fix(ivt(p)), c_fix(ivt(p)), & + big_cost, crootfr(p,j), s_fix(ivt(p)), tc_soisno(c,j)) + case ('Bytnerowicz') ! no acclimation calculation + costNit(j,icostFix) = fun_cost_fix_Bytnerowicz_noAcc(fixer, & + params_inst%nfix_tmin(ivt(p)), params_inst%nfix_topt(ivt(p)), params_inst%nfix_tmax(ivt(p)), & + big_cost,crootfr(p,j), s_fix(ivt(p)), tc_soisno(c,j)) + case default + errCode = ' ERROR: unknown nfix_method value: ' // nfix_method + call endrun( msg=trim(errCode) // errMsg(sourcefile, __LINE__)) + end select + end do cost_fix(p,1:nlevdecomp) = costNit(:,icostFix) !-------------------------------------------------------------------- - !------------ ! If passive uptake is insufficient, consider fixation, ! mycorrhizal ! non-mycorrhizal, storage, and retranslocation. !-------------------------------------------------------------------- - !------------ !-------------------------------------------------------------------- - !------------ ! Costs of active uptake. !-------------------------------------------------------------------- - !------------ !------Mycorrhizal Uptake Cost-----------------! do j = 1,nlevdecomp rootc_dens_step = rootc_dens(p,j) * permyc(p,istp) @@ -1608,6 +1691,44 @@ real(r8) function fun_cost_fix(fixer,a_fix,b_fix,c_fix,big_cost,crootfr,s_fix, t end if ! ends up with the fixer or non-fixer decision end function fun_cost_fix + + +!========================================================================================= + real(r8) function fun_cost_fix_Bytnerowicz_noAcc(fixer,nfix_tmin,nfix_topt,nfix_tmax,big_cost,crootfr,s_fix, tc_soisno) + +! Description: +! Calculate the cost of fixing N by nodules. +! Code Description: +! This code is written to CTSM5.1 by Will Wieder 11/17/2022, modified for CLM6 11/01/2024 + + implicit none +!-------------------------------------------------------------------------- +! Function result. +!-------------------------------------------------------------------------- +! real(r8) , intent(out) :: cost_of_n !!! cost of fixing N (kgC/kgN) +!-------------------------------------------------------------------------- + integer, intent(in) :: fixer ! flag indicating if plant is a fixer + ! 1=yes, otherwise no. + real(r8), intent(in) :: nfix_tmin ! As in Bytnerowicz et al. (2022) + real(r8), intent(in) :: nfix_topt ! As in Bytnerowicz et al. (2022) + real(r8), intent(in) :: nfix_tmax ! As in Bytnerowicz et al. (2022) + real(r8), intent(in) :: big_cost ! an arbitrary large cost (gC/gN) + real(r8), intent(in) :: crootfr ! fraction of roots for carbon that are in this layer + real(r8), intent(in) :: s_fix ! Inverts the temperature function for a cost function + real(r8), intent(in) :: tc_soisno ! soil temperature (degrees Celsius) + + if (fixer == 1 .and. crootfr > 1.e-6_r8 .and. tc_soisno > nfix_tmin .and. tc_soisno < nfix_tmax) then + fun_cost_fix_Bytnerowicz_noAcc = (-s_fix) / ( ((nfix_tmax-tc_soisno)/(nfix_tmax-nfix_topt))*& + ( ((tc_soisno-nfix_tmin)/(nfix_topt-nfix_tmin))**& + ((nfix_topt- nfix_tmin)/(nfix_tmax-nfix_topt)) ) ) + fun_cost_fix_Bytnerowicz_noAcc = min(fun_cost_fix_Bytnerowicz_noAcc,big_cost) + else + fun_cost_fix_Bytnerowicz_noAcc = big_cost + end if ! ends up with the fixer or non-fixer decision + + end function fun_cost_fix_Bytnerowicz_noAcc +!========================================================================================= + !========================================================================================= real(r8) function fun_cost_active(sminn_layer,big_cost,kc_active,kn_active,rootc_dens,crootfr,smallValue) diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90 index 3f5c58ac0e..e8121519aa 100644 --- a/src/main/controlMod.F90 +++ b/src/main/controlMod.F90 @@ -119,6 +119,7 @@ subroutine control_init(dtime) ! ! !USES: use CNMRespMod , only : CNMRespReadNML + use CNFUNMod , only : CNFUNReadNML use CNNDynamicsMod , only : CNNDynamicsReadNML use CNPhenologyMod , only : CNPhenologyReadNML use landunit_varcon , only : max_lunit @@ -596,6 +597,7 @@ subroutine control_init(dtime) if ( use_fun ) then call CNMRespReadNML( NLFilename ) + call CNFUNReadNML( NLFilename ) end if call soilHydReadNML( NLFilename )