From ec31fea4fc8f38eb6710044cf75b0f2413d2b993 Mon Sep 17 00:00:00 2001 From: Marcos Longo Date: Mon, 16 Jun 2025 16:02:07 -0700 Subject: [PATCH 1/3] Consolidated the hydraulic failure mortality parameters. The original code had two parameters for defining hydraulic failure thresholds, one when FATES-Hydro was enabled (fates_mort_hf_flc_threshold) enabled, and the other when not using FATES-Hydro (fates_mort_hf_sm_threshold). This PR replaces these parameters with a single one (fates_mort_hydrfail_threshold) that works for both FATES-Hydro off and on. When FATES-Hydro is on, btran is equivalent to the fraction of maximum conductivity, though in the code we still calculate the fraction. One additional change is that hydraulic failure mortality would increase linearly as conductivity decreased in FATES-Hydro, but would be a step function in non-Hydro settings. This PR removes the step function and uses the linear decrease in all simulations. Because of this change, this PR will NOT be bit-for-bit. --- biogeochem/EDMortalityFunctionsMod.F90 | 61 +++++++++++++----------- main/EDPftvarcon.F90 | 39 ++++++--------- parameter_files/fates_params_default.cdl | 14 ++---- 3 files changed, 51 insertions(+), 63 deletions(-) diff --git a/biogeochem/EDMortalityFunctionsMod.F90 b/biogeochem/EDMortalityFunctionsMod.F90 index 312b01bb83..d02881e1bb 100644 --- a/biogeochem/EDMortalityFunctionsMod.F90 +++ b/biogeochem/EDMortalityFunctionsMod.F90 @@ -86,8 +86,9 @@ subroutine mortality_rates( cohort_in,bc_in, btran_ft, mean_temp, & real(r8) :: target_leaf_c ! target leaf biomass for the current trim status and ! damage class [kgC] real(r8) :: store_c - real(r8) :: hf_sm_threshold ! hydraulic failure soil moisture threshold - real(r8) :: hf_flc_threshold ! hydraulic failure fractional loss of conductivity threshold + real(r8) :: mort_hydrfailure_threshold ! hydraulic failure threshold (btran units). When + ! FATES-Hydro is enabled, this is equivalent to + ! fraction of maximum conductivity (fmc) real(r8) :: mort_ip_size_senescence ! inflection point for increase in mortality with dbh real(r8) :: mort_r_size_senescence ! rate of mortality increase with dbh in senesence term real(r8) :: mort_ip_age_senescence ! inflection point for increase in mortality with age @@ -98,7 +99,6 @@ subroutine mortality_rates( cohort_in,bc_in, btran_ft, mean_temp, & real(r8) :: min_fmc_tr ! minimum fraction of maximum conductivity for transporting root real(r8) :: min_fmc_ar ! minimum fraction of maximum conductivity for absorbing root real(r8) :: min_fmc ! minimum fraction of maximum conductivity for whole plant - real(r8) :: flc ! fractional loss of conductivity logical :: is_decid_dormant ! Flag to signal that the cohort is deciduous and dormant @@ -160,36 +160,41 @@ subroutine mortality_rates( cohort_in,bc_in, btran_ft, mean_temp, & bmort = EDPftvarcon_inst%bmort(cohort_in%pft) ! Proxy for hydraulic failure induced mortality. - hf_sm_threshold = EDPftvarcon_inst%hf_sm_threshold(cohort_in%pft) - hf_flc_threshold = EDPftvarcon_inst%hf_flc_threshold(cohort_in%pft) - - if (hlm_use_planthydro == itrue) then - !note the flc is set as the fraction of max conductivity in hydro - min_fmc_ag = minval(cohort_in%co_hydr%ftc_ag(:)) - min_fmc_tr = cohort_in%co_hydr%ftc_troot - min_fmc_ar = minval(cohort_in%co_hydr%ftc_aroot(:)) - min_fmc = min(min_fmc_ag, min_fmc_tr) - min_fmc = min(min_fmc, min_fmc_ar) - flc = 1.0_r8-min_fmc - if(flc >= hf_flc_threshold .and. hf_flc_threshold < 1.0_r8 )then - hmort = (flc-hf_flc_threshold)/(1.0_r8-hf_flc_threshold) * & - EDPftvarcon_inst%mort_scalar_hydrfailure(cohort_in%pft) + mort_hydrfailure_threshold = EDPftvarcon_inst%mort_hydrfailure_threshold(cohort_in%pft) + + ! Make sure the hydraulic failure mortality threshold is positive. In case it is + ! zero, we bypass the mortality calculation altogether. + if ( mort_hydrfailure_threshold > nearzero ) then + ! We calculate the fraction of maximum conductivity differently, depending on + ! whether FATES-Hydro is enabled or disabled. + if (hlm_use_planthydro == itrue) then + ! FATES-Hydro enabled: set fmc as the fraction of max conductivity in hydro + min_fmc_ag = minval(cohort_in%co_hydr%ftc_ag(:)) + min_fmc_tr = cohort_in%co_hydr%ftc_troot + min_fmc_ar = minval(cohort_in%co_hydr%ftc_aroot(:)) + min_fmc = min(min_fmc_ag, min_fmc_tr) + min_fmc = min(min_fmc, min_fmc_ar) + else if ( (.not. is_decid_dormant) .and. & + ( ( minval(bc_in%t_soisno_sl) - tfrz ) > soil_tfrz_thresh ) ) then + ! FATES-Hydro disabled: set fmc as btran + min_fmc = btran_ft(cohort_in%pft) else - hmort = 0.0_r8 - endif + ! Dormant plant, or plant in frozen soils, assume conductivity at the threshold, + ! which effectively sets mortality to zero. + min_fmc = mort_hydrfailure_threshold + end if + + ! Hydraulic failure mortality increases as fmc decreases. Note that this is different + ! than before when FATES-Hydro was disabled, in which mortality followed a step function. + hmort = max( 0.0_r8, ( mort_hydrfail_threshold - min_fmc ) / mort_hydrfail_threshold ) * & + EDPftvarcon_inst%mort_scalar_hydrfailure(cohort_in%pft) else - ! When FATES-Hydro is off, hydraulic failure mortality occurs only when btran - ! falls below a threshold and plants have leaves. - if ( (.not. is_decid_dormant) .and. & - ( btran_ft(cohort_in%pft) <= hf_sm_threshold ) .and. & - ( ( minval(bc_in%t_soisno_sl) - tfrz ) > soil_tfrz_thresh ) ) then - hmort = EDPftvarcon_inst%mort_scalar_hydrfailure(cohort_in%pft) - else - hmort = 0.0_r8 - end if + ! Assume zero hydraulic failure mortality. + hmort = 0.0_r8 end if + ! Carbon Starvation induced mortality. if ( cohort_in%dbh > 0._r8 ) then diff --git a/main/EDPftvarcon.F90 b/main/EDPftvarcon.F90 index 835ffab36f..1ecbbe9520 100644 --- a/main/EDPftvarcon.F90 +++ b/main/EDPftvarcon.F90 @@ -87,10 +87,10 @@ module EDPftvarcon real(r8), allocatable :: mort_r_age_senescence(:) ! rate of change in mortality with age real(r8), allocatable :: mort_scalar_coldstress(:) ! maximum mortality rate from cold stress real(r8), allocatable :: mort_scalar_cstarvation(:) ! maximum mortality rate from carbon starvation - real(r8), allocatable :: mort_scalar_hydrfailure(:) ! maximum mortality rate from hydraulic failure real(r8), allocatable :: mort_upthresh_cstarvation(:) ! threshold for storage biomass (relative to target leaf biomass) above which carbon starvation is zero - real(r8), allocatable :: hf_sm_threshold(:) ! soil moisture (btran units) at which drought mortality begins for non-hydraulic model - real(r8), allocatable :: hf_flc_threshold(:) ! plant fractional loss of conductivity at which drought mortality begins for hydraulic model + real(r8), allocatable :: mort_scalar_hydrfailure(:) ! maximum mortality rate from hydraulic failure + real(r8), allocatable :: mort_hydrfailure_threshold(:) ! threshold (btran units) below which drought mortality begins. For FATES-Hydro, this is numerically + ! equivalent to the fraction of total conductivity (or one minus fraction of loss of conductivity) real(r8), allocatable :: germination_rate(:) ! Fraction of seed mass germinating per year (yr-1) real(r8), allocatable :: seed_decay_rate(:) ! Fraction of seed mass (both germinated and @@ -510,6 +510,10 @@ subroutine Register_PFT(this, fates_params) call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) + name = 'fates_mort_hydrfailure_threshold' + call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & + dimension_names=dim_names, lower_bounds=dim_lower_bound) + name = 'fates_mort_r_size_senescence' call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) @@ -542,14 +546,6 @@ subroutine Register_PFT(this, fates_params) call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) - name = 'fates_mort_hf_sm_threshold' - call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & - dimension_names=dim_names, lower_bounds=dim_lower_bound) - - name = 'fates_mort_hf_flc_threshold' - call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & - dimension_names=dim_names, lower_bounds=dim_lower_bound) - name = 'fates_recruit_seed_germination_rate' call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) @@ -927,14 +923,17 @@ subroutine Receive_PFT(this, fates_params) call fates_params%RetrieveParameterAllocate(name=name, & data=this%mort_scalar_cstarvation) + name = 'fates_mort_upthresh_cstarvation' + call fates_params%RetrieveParameterAllocate(name=name, & + data=this%mort_upthresh_cstarvation) + name = 'fates_mort_scalar_hydrfailure' call fates_params%RetrieveParameterAllocate(name=name, & data=this%mort_scalar_hydrfailure) - name = 'fates_mort_upthresh_cstarvation' + name = 'fates_mort_hydrfailure_threshold' call fates_params%RetrieveParameterAllocate(name=name, & - data=this%mort_upthresh_cstarvation) - + data=this%mort_hydrfailure_threshold) name = 'fates_mort_ip_size_senescence' call fates_params%RetrieveParameterAllocate(name=name, & @@ -964,15 +963,6 @@ subroutine Receive_PFT(this, fates_params) call fates_params%RetrieveParameterAllocate(name=name, & data=this%mort_upthresh_cstarvation) - - name = 'fates_mort_hf_sm_threshold' - call fates_params%RetrieveParameterAllocate(name=name, & - data=this%hf_sm_threshold) - - name = 'fates_mort_hf_flc_threshold' - call fates_params%RetrieveParameterAllocate(name=name, & - data=this%hf_flc_threshold) - name = 'fates_recruit_seed_germination_rate' call fates_params%RetrieveParameterAllocate(name=name, & data=this%germination_rate) @@ -1603,8 +1593,7 @@ subroutine FatesReportPFTParams(is_master) write(fates_log(),fmt0) 'mort_scalar_cstarvation = ',EDPftvarcon_inst%mort_scalar_cstarvation write(fates_log(),fmt0) 'mort_scalar_hydrfailure = ',EDPftvarcon_inst%mort_scalar_hydrfailure write(fates_log(),fmt0) 'mort_upthresh_cstarvation = ',EDPftvarcon_inst%mort_upthresh_cstarvation - write(fates_log(),fmt0) 'hf_sm_threshold = ',EDPftvarcon_inst%hf_sm_threshold - write(fates_log(),fmt0) 'hf_flc_threshold = ',EDPftvarcon_inst%hf_flc_threshold + write(fates_log(),fmt0) 'mort_hydrfailure_threshold = ',EDPftvarcon_inst%mort_hydrfailure_threshold write(fates_log(),fmt0) 'germination_timescale = ',EDPftvarcon_inst%germination_rate write(fates_log(),fmt0) 'seed_decay_turnover = ',EDPftvarcon_inst%seed_decay_rate diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 9fb97c811f..b67a42e43c 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -444,12 +444,9 @@ variables: double fates_mort_freezetol(fates_pft) ; fates_mort_freezetol:units = "degrees C" ; fates_mort_freezetol:long_name = "minimum temperature tolerance" ; - double fates_mort_hf_flc_threshold(fates_pft) ; - fates_mort_hf_flc_threshold:units = "fraction" ; - fates_mort_hf_flc_threshold:long_name = "plant fractional loss of conductivity at which drought mortality begins for hydraulic model" ; - double fates_mort_hf_sm_threshold(fates_pft) ; - fates_mort_hf_sm_threshold:units = "unitless" ; - fates_mort_hf_sm_threshold:long_name = "soil moisture (btran units) at which drought mortality begins for non-hydraulic model" ; + double fates_mort_hydrfail_threshold(fates_pft) ; + fates_mort_hydrfail_threshold:units = "fraction" ; + fates_mort_hydrfail_threshold:long_name = "threshold (btran units) below which hydraulic failure mortality begins. When running FATES-Hydro, this is numerically equivalent to fraction of total conductivity (or one minus fraction of loss of conductivity)." ; double fates_mort_ip_age_senescence(fates_pft) ; fates_mort_ip_age_senescence:units = "years" ; fates_mort_ip_age_senescence:long_name = "Mortality cohort age senescence inflection point. If _ this mortality term is off. Setting this value turns on age dependent mortality. " ; @@ -1423,12 +1420,9 @@ data: fates_mort_freezetol = 2.5, -55, -80, -30, 2.5, -80, -60, -10, -80, -71, -95, -89, -20, 2.5 ; - fates_mort_hf_flc_threshold = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + fates_mort_hydrfail_threshold = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ; - fates_mort_hf_sm_threshold = 1e-06, 1e-06, 1e-06, 1e-06, 1e-06, 1e-06, - 1e-06, 1e-06, 1e-06, 1e-06, 1e-06, 1e-06, 1e-06, 1e-06 ; - fates_mort_ip_age_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; fates_mort_ip_size_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; From 1c4f3e58cf0cabee02a14e6dd745f1cc632bfac3 Mon Sep 17 00:00:00 2001 From: Marcos Longo Date: Wed, 18 Jun 2025 08:28:01 -0700 Subject: [PATCH 2/3] Fix typo in the new variable name. --- biogeochem/EDMortalityFunctionsMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDMortalityFunctionsMod.F90 b/biogeochem/EDMortalityFunctionsMod.F90 index d02881e1bb..8bd66568a0 100644 --- a/biogeochem/EDMortalityFunctionsMod.F90 +++ b/biogeochem/EDMortalityFunctionsMod.F90 @@ -186,7 +186,7 @@ subroutine mortality_rates( cohort_in,bc_in, btran_ft, mean_temp, & ! Hydraulic failure mortality increases as fmc decreases. Note that this is different ! than before when FATES-Hydro was disabled, in which mortality followed a step function. - hmort = max( 0.0_r8, ( mort_hydrfail_threshold - min_fmc ) / mort_hydrfail_threshold ) * & + hmort = max( 0.0_r8, ( mort_hydrfailure_threshold - min_fmc ) / mort_hydrfailure_threshold ) * & EDPftvarcon_inst%mort_scalar_hydrfailure(cohort_in%pft) else From 4ebf1f7b47e1efb98b4335c4bf3ef21dc98eacae Mon Sep 17 00:00:00 2001 From: Marcos Longo Date: Wed, 18 Jun 2025 09:46:46 -0700 Subject: [PATCH 3/3] Fix new variable name in fates_params_default.cdl --- parameter_files/fates_params_default.cdl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index b67a42e43c..67264ca174 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -444,9 +444,9 @@ variables: double fates_mort_freezetol(fates_pft) ; fates_mort_freezetol:units = "degrees C" ; fates_mort_freezetol:long_name = "minimum temperature tolerance" ; - double fates_mort_hydrfail_threshold(fates_pft) ; - fates_mort_hydrfail_threshold:units = "fraction" ; - fates_mort_hydrfail_threshold:long_name = "threshold (btran units) below which hydraulic failure mortality begins. When running FATES-Hydro, this is numerically equivalent to fraction of total conductivity (or one minus fraction of loss of conductivity)." ; + double fates_mort_hydrfailure_threshold(fates_pft) ; + fates_mort_hydrfailure_threshold:units = "fraction" ; + fates_mort_hydrfailure_threshold:long_name = "threshold (btran units) below which hydraulic failure mortality begins. When running FATES-Hydro, this is numerically equivalent to fraction of maximum conductivity (or one minus fraction of loss of conductivity)." ; double fates_mort_ip_age_senescence(fates_pft) ; fates_mort_ip_age_senescence:units = "years" ; fates_mort_ip_age_senescence:long_name = "Mortality cohort age senescence inflection point. If _ this mortality term is off. Setting this value turns on age dependent mortality. " ; @@ -1420,7 +1420,7 @@ data: fates_mort_freezetol = 2.5, -55, -80, -30, 2.5, -80, -60, -10, -80, -71, -95, -89, -20, 2.5 ; - fates_mort_hydrfail_threshold = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + fates_mort_hydrfailure_threshold = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ; fates_mort_ip_age_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ;