From 27b6376dcc28b0b34f8c6451e9c14bc15f228953 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 16 Jan 2025 14:40:14 -0500 Subject: [PATCH 001/194] increase ncl max from 2 to 3 --- main/EDParamsMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/EDParamsMod.F90 b/main/EDParamsMod.F90 index cc906fecef..7defaa7f60 100644 --- a/main/EDParamsMod.F90 +++ b/main/EDParamsMod.F90 @@ -104,7 +104,7 @@ module EDParamsMod real(r8), parameter, public :: soil_tfrz_thresh = -2.0_r8 ! Soil temperature threshold below which hydraulic failure mortality is off (non-hydro only) in degrees C - integer, parameter, public :: nclmax = 2 ! Maximum number of canopy layers (used only for scratch arrays) + integer, parameter, public :: nclmax = 3 ! Maximum number of canopy layers (used only for scratch arrays) ! We would make this even higher, but making this ! a little lower keeps the size down on some output arrays ! For large arrays at patch level we use dynamic allocation From 7ad9baca4c4d19c3c190514ca4ea4037836fb449 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 16 Jan 2025 16:43:58 -0500 Subject: [PATCH 002/194] removed incorrect comment --- main/EDParamsMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/EDParamsMod.F90 b/main/EDParamsMod.F90 index 7defaa7f60..db1b755c04 100644 --- a/main/EDParamsMod.F90 +++ b/main/EDParamsMod.F90 @@ -104,7 +104,7 @@ module EDParamsMod real(r8), parameter, public :: soil_tfrz_thresh = -2.0_r8 ! Soil temperature threshold below which hydraulic failure mortality is off (non-hydro only) in degrees C - integer, parameter, public :: nclmax = 3 ! Maximum number of canopy layers (used only for scratch arrays) + integer, parameter, public :: nclmax = 3 ! Maximum number of canopy layers allowed ! We would make this even higher, but making this ! a little lower keeps the size down on some output arrays ! For large arrays at patch level we use dynamic allocation From b5e7aebe95b074b895aeae230e0dbb16d4f26270 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Sun, 2 Mar 2025 21:17:57 -0800 Subject: [PATCH 003/194] add burn window subroutine --- fire/SFFireWeatherMod.F90 | 41 +++++++++++++++++++++++++++++++++++++++ fire/SFNesterovMod.F90 | 1 + 2 files changed, 42 insertions(+) diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 3191b460b1..b9178a53b1 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -9,12 +9,14 @@ module SFFireWeatherMod real(r8) :: fire_weather_index ! fire weather index real(r8) :: effective_windspeed ! effective wind speed, corrected for by tree/grass cover [m/min] + integer :: rx_flag ! prescribed fire burn window flag[1=burn window present; 0=no burn window] contains procedure(initialize_fire_weather), public, deferred :: Init procedure(update_fire_weather), public, deferred :: UpdateIndex procedure, public :: UpdateEffectiveWindSpeed + procedure, public :: UpdateRxfireBurnWindow end type fire_weather @@ -67,4 +69,43 @@ subroutine UpdateEffectiveWindSpeed(this, wind_speed, tree_fraction, grass_fract end subroutine UpdateEffectiveWindSpeed + subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up, & + temp_low,rh_up, rh_low, wind_up, wind_low) + + ! ARGUMENTS + class(fire_weather), intent(inout) :: this ! fire weather class + real(r8), intent(in) :: temp_C ! daily averaged temperature [degrees C] + logical, intent(in) :: rxfire_switch ! whether prescribed fire is turned on + real(r8), intent(in) :: rh ! daily relative humidity [%] + real(r8), intent(in) :: wind ! wind speed [m/min] + real(r8), intent(in) :: temp_up ! user defined upper bound for temp when define a burn window + real(r8), intent(in) :: temp_low ! user defined lower bound for temp when define a burn window + real(r8), intent(in) :: rh_up ! user defined upper bound for relative humidity + real(r8), intent(in) :: rh_low ! user defined lower bound for relative humidity + real(r8), intent(in) :: wind_up ! user defined upper bound for wind speed + real(r8), intent(in) :: wind_low ! user defined lower bound for wind speed + + !LOCAL VARIABLES + real(r8) :: t_check !intermediate value derived from temp condition check + real(r8) :: rh_check !intermediate value derived from RH condition check + real(r8) :: ws_check !intermediate value derived from wind speed condition check + + if(.not. rxfire_switch) return + + t_check = (temp_C - temp_low)*(temp_C - temp_up) + rh_check = (rh - rh_low)*(rh - rh_up) + ws_check = (wind - wind_low)*(wind - wind_up) + + if(t_check .le. 0.0_r8 .and. rh_check .le. 0.0_r8 .and. & + ws_check .le. 0.0_r8)then + this%rx_flag = 1 + end if + + end subroutine UpdateRxfireBurnWindow + + + + + + end module SFFireWeatherMod \ No newline at end of file diff --git a/fire/SFNesterovMod.F90 b/fire/SFNesterovMod.F90 index 23128c880f..2a0147058b 100644 --- a/fire/SFNesterovMod.F90 +++ b/fire/SFNesterovMod.F90 @@ -33,6 +33,7 @@ subroutine init_nesterov_fire_weather(this) ! initialize values to 0.0 this%fire_weather_index = 0.0_r8 this%effective_windspeed = 0.0_r8 + this%rx_flag = 0 end subroutine init_nesterov_fire_weather From f8685e53c9f31465961108a09a585715669c3893 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Sun, 2 Mar 2025 21:18:31 -0800 Subject: [PATCH 004/194] added decision tree to decide which fire happens --- fire/SFMainMod.F90 | 80 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 3068f34d22..a9c9abb6b2 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -66,6 +66,7 @@ subroutine DailyFireModel(currentSite, bc_in) call CalculateSurfaceRateOfSpread(currentSite) call CalculateSurfaceFireIntensity(currentSite) call CalculateAreaBurnt(currentSite) + call CalculateRxfireAreaBurnt(currentSite) call crown_scorching(currentSite) call crown_damage(currentSite) call cambial_damage_kill(currentSite) @@ -79,7 +80,7 @@ end subroutine DailyFireModel subroutine UpdateFireWeather(currentSite, bc_in) ! ! DESCRIPTION: - ! Updates the site's fire weather index and calculates effective windspeed based on + ! Updates the site's fire weather index, burn window for prescribed fire, and calculates effective windspeed based on ! vegetation characteristics ! ! Currently we use tree and grass fraction averaged over whole grid (site) to @@ -88,6 +89,9 @@ subroutine UpdateFireWeather(currentSite, bc_in) use FatesConstantsMod, only : tfrz => t_water_freeze_k_1atm use FatesConstantsMod, only : sec_per_day, sec_per_min use EDTypesMod, only : CalculateTreeGrassAreaSite + use EDParamsMod, only : rxfire_switch + use SFParamsMod, only : SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, & + SF_val_rxfire_rhlw, SF_val_rxfire_wdup, SF_val_rxfire_wdlw ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -129,12 +133,19 @@ subroutine UpdateFireWeather(currentSite, bc_in) ! update fire weather index call currentSite%fireWeather%UpdateIndex(temp_C, precip, rh, wind) + ! update prescribed fire burn window + call currentSite%fireWeather%UpdateRxfireBurnWindow(rxfire_switch, temp_C, rh, wind, & + SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, SF_val_rxfire_rhlw, & + SF_val_rxfire_wdup, SF_val_rxfire_wdlw) + + ! calculate site-level tree, grass, and bare fraction call CalculateTreeGrassAreaSite(currentSite, tree_fraction, grass_fraction, bare_fraction) ! update effective wind speed call currentSite%fireWeather%UpdateEffectiveWindSpeed(wind*sec_per_min, tree_fraction, & grass_fraction, bare_fraction) + end subroutine UpdateFireWeather @@ -349,12 +360,16 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! ! DESCRIPTION: ! Calculates surface fireline intensity for each patch of a site + ! Use calculated fire intensity to determine if prescribed fire or + ! wildfire happens ! Right now also calculates the area burnt... ! use FatesConstantsMod, only : m2_per_km2 use SFEquationsMod, only : FireDuration, LengthToBreadth use SFEquationsMod, only : AreaBurnt, FireSize, FireIntensity - use SFParamsMod, only : SF_val_fire_threshold + use SFParamsMod, only : SF_val_fire_threshold, SF_val_rxfire_minthreshold, & + SF_val_rxfire_maxthreshold, SF_val_rxfire_fuel_min, SF_val_rxfire_fuel_max + use EDParamsMod, only : rxfire_switch ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -366,6 +381,12 @@ subroutine CalculateSurfaceFireIntensity(currentSite) real(r8) :: length_to_breadth ! length to breadth ratio of fire ellipse (unitless) real(r8) :: fire_size ! size of fire [m2] real(r8) :: area_burnt ! area burnt [m2/km2] + logical :: is_rxfire ! is it a prescribed fire? + logical :: rx_man ! prescribed fire use human ignition + logical :: rx_hyb ! prescribed fire due to both lightning strike and human ignition + logical :: managed_wildfire ! is it a wildfire with FI lower than the max rxfire intensity?[can either be Rx fire or wildfire] + logical :: true_wildfire ! is it a wildfire that cannot be managed? + logical :: is_wildfire ! combine both managed and true wildfire for now currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -384,17 +405,62 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! initialize patch parameters to zero currentPatch%FI = 0.0_r8 currentPatch%fire = 0 + currentPatch%rxfire = 0 + currentPatch%rxfire_FI = 0.0_r8 - if (currentSite%NF > 0.0_r8) then + if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag .eq. itrue) then ! fire intensity [kW/m] currentPatch%FI = FireIntensity(currentPatch%TFC_ROS/0.45_r8, currentPatch%ROS_front/60.0_r8) - ! track fires greater than kW/m energy threshold - if (currentPatch%FI > SF_val_fire_threshold) then - currentPatch%fire = 1 + ! Decide if prescribed fire or wildfire happen + ! prescribed fire and wildfire cannot happen on the same patch + + rx_man = (currentPatch%FI > SF_val_rxfire_minthreshold .and. & + currentPatch%FI < SF_val_rxfire_maxthreshold .and. & + currentSite%NF == 0.0_r8) + + rx_hyb = (currentPatch%FI < SF_val_fire_threshold .and. & + currentPatch%FI > SF_val_rxfire_minthreshold .and. & + currentPatch%FI < SF_val_rxfire_maxthreshold .and. & + currentSite%NF > 0.0_r8) + + is_rxfire = (rx_man .or. rx_hyb) + + managed_wildfire = (currentSite%NF > 0.0_r8 .and. & + currentPatch%FI > SF_val_fire_threshold .and. & + currentPatch%FI < SF_val_rxfire_maxthreshold) + + true_wildfire = (currentSite%NF > 0.0_r8 .and. & + currentPatch%FI > SF_val_fire_threshold .and. & + currentPatch%FI > SF_val_rxfire_maxthreshold) + + is_wildfire = (managed_wildfire .or. true_wildfire) + + if (currentSite%fireWeather%rx_flag == itrue .and. & ! burn window check + currentPatch%fuel%non_trunk_loading > SF_val_rxfire_fuel_min .and. & ! fuel load check + currentPatch%fuel%non_trunk_loading < SF_val_rxfire_fuel_max) then + currentSite%rxfire_area_fuel = currentSite%rxfire_area_fuel + currentPatch%area ! record burnable area after fuel load check + if (is_rxfire) then + currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check + currentPatch%rxfire = 1 + else if (is_wildfire) then + currentPatch%fire = 1 + end if + + else ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on + ! track wildfires greater than kW/m energy threshold + if (currentPatch%FI > SF_val_fire_threshold) then + currentPatch%fire = 1 + end if + + end if + + if (currentPatch%fire == itrue) then currentSite%NF_successful = currentSite%NF_successful + & - currentSite%NF*currentSite%FDI*currentPatch%area/area + currentSite%NF*currentSite%FDI*currentPatch%area/area + else if (currentPatch%rxfire == itrue) then + currentPatch%rxfire_FI = currentPatch%FI end if end if From 516e08425391841419c3b8096d8e7bd691479f1e Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Mon, 3 Mar 2025 10:30:35 -0800 Subject: [PATCH 005/194] add rxfire burnt area and post-fire mortality --- fire/SFMainMod.F90 | 76 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index a9c9abb6b2..3129061b90 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -37,6 +37,7 @@ module SFMainMod implicit none private + public :: DailyFireModel public :: UpdateFuelCharacteristics @@ -415,6 +416,8 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! Decide if prescribed fire or wildfire happen ! prescribed fire and wildfire cannot happen on the same patch + + ! store some contion check here to simplify decision tree rx_man = (currentPatch%FI > SF_val_rxfire_minthreshold .and. & currentPatch%FI < SF_val_rxfire_maxthreshold .and. & @@ -465,6 +468,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) end if end if + currentPatch => currentPatch%younger end do @@ -529,6 +533,57 @@ subroutine CalculateAreaBurnt(currentSite) end subroutine CalculateAreaBurnt !--------------------------------------------------------------------------------------- + + !***************************************************************** + subroutine CalculateRxfireAreaBurnt ( currentSite ) + !***************************************************************** + + !returns burned fraction for prescribed fire per patch by first checking + !if total burnable fraction at site level is greater than user defined fraction of site area + !if yes, calculate burned fraction as (user defined frac / total burnable frac) + + use SFParamsMod, only : SF_val_rxfire_AB !user defined prescribed fire area in fraction per day to reflect burning capacity + + ! ARGUMENTS + type(ed_site_type), intent(inout), target :: currentSite + + !LOCALS + type(fates_patch_type), pointer :: currentPatch + + real(r8) :: total_burnable_frac ! total fractional land area that can apply prescribed fire after condition checks at site level + + ! Testing parameters + real(r8), parameter :: min_frac_site = 0.1_r8 + + ! initialize site variables + currentSite%rxfire_area_final = 0.0_r8 + total_burnable_frac = 0.0_r8 + + ! update total burnable fraction + total_burnable_frac = currentSite%rxfire_area_fi / AREA + + currentPatch => currentSite%oldest_patch; + + do while(associated(currentPatch)) + + if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then + currentPatch%rxfire_frac_burnt = 0.0_r8 + if (currentPatch%rxfire .eq. itrue .and. & + total_burnable_frac .ge. min_frac_site ) then + currentSite%rxfire_area_final = currentSite%rxfire_area_final + currentPatch%area ! the final burned total land area + currentPatch%rxfire_frac_burnt = min(0.99_r8, (SF_val_rxfire_AB / total_burnable_frac)) + else + currentPatch%rxfire = 0 ! update rxfire occurence at patch + currentPatch%rxfire_FI = 0.0_r8 + end if + end if + + currentPatch => currentPatch%younger; + end do ! end patch loop + + +!--------------------------------------------------------------------------------------- + !***************************************************************** subroutine crown_scorching ( currentSite ) @@ -556,7 +611,7 @@ subroutine crown_scorching ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then tree_ag_biomass = 0.0_r8 - if (currentPatch%fire == 1) then + if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then currentCohort => currentPatch%tallest; do while(associated(currentCohort)) if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only @@ -612,7 +667,7 @@ subroutine crown_damage ( currentSite ) do while(associated(currentPatch)) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1) then + if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then currentCohort=>currentPatch%tallest @@ -682,7 +737,7 @@ subroutine cambial_damage_kill ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1) then + if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then currentCohort => currentPatch%tallest; do while(associated(currentCohort)) if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only @@ -735,11 +790,14 @@ subroutine post_fire_mortality ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1) then + if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then currentCohort => currentPatch%tallest do while(associated(currentCohort)) currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 + currentCohort%rxfire_mort = 0.0_r8 + currentCohort%rxcrownire_mort = 0.0_r8 + currentCohort%rxcambial_mort = 0.0_r8 if ( prt_params%woody(currentCohort%pft) == itrue) then ! Equation 22 in Thonicke et al. 2010. currentCohort%crownfire_mort = EDPftvarcon_inst%crown_kill(currentCohort%pft)*currentCohort%fraction_crown_burned**3.0_r8 @@ -750,6 +808,16 @@ subroutine post_fire_mortality ( currentSite ) currentCohort%fire_mort = 0.0_r8 !Set to zero. Grass mode of death is removal of leaves. endif !trees + ! now decide which type of post-fire mortality, prescribed fire or wildfire? + if (currentPatch%rxfire == itrue .and. currentPatch%fire == ifalse) then + currentCohort%rxfire_mort = currentCohort%fire_mort + currentCohort%rxcrownire_mort = currentCohort%crownfire_mort + currentCohort%rxcambial_mort = currentCohort%cambial_mort + currentCohort%fire_mort = 0.0_r8 + currentCohort%crownfire_mort = 0.0_r8 + currentCohort%cambial_mort = 0.0_r8 + end if + currentCohort => currentCohort%shorter enddo !end cohort loop From 954d6cd1c14db845d01652187f047669cf14f538 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 4 Mar 2025 09:40:07 -0800 Subject: [PATCH 006/194] adding tools-fates-xarray as submodule --- .gitmodules | 3 +++ tools/xarray | 1 + 2 files changed, 4 insertions(+) create mode 160000 tools/xarray diff --git a/.gitmodules b/.gitmodules index 7dc4d3c410..cb98a15ece 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/tools-fates-landusedata +[submodule "tools/xarray"] + path = tools/xarray + url = git@github.com:NGEET/tools-fates-xarray.git diff --git a/tools/xarray b/tools/xarray new file mode 160000 index 0000000000..61b88ca7c2 --- /dev/null +++ b/tools/xarray @@ -0,0 +1 @@ +Subproject commit 61b88ca7c2beb5751645b8f779a5541e86e50896 From 08302ca855118a506eeff00ca4bc6c0678080f9b Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 13:31:32 -0800 Subject: [PATCH 007/194] cohort rxfire mortality --- biogeochem/EDCohortDynamicsMod.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index bdb7cee9cf..a8bdc80d3d 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1090,6 +1090,9 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%fire_mort = (currentCohort%n*currentCohort%fire_mort + & nextc%n*nextc%fire_mort)/newn + + currentCohort%rxfire_mort = (currentCohort%n*currentCohort%rxfire_mort + & + nextc%n*nextc%rxfire_mort)/newn ! mortality diagnostics currentCohort%cmort = (currentCohort%n*currentCohort%cmort + nextc%n*nextc%cmort)/newn From fae8b9ccb88d3b9a7752d82f6f7b83e07505b204 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 13:31:51 -0800 Subject: [PATCH 008/194] patch level dynamic due to rxfire --- biogeochem/EDPatchDynamicsMod.F90 | 52 ++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index f68f49b894..4ae30c4108 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -434,12 +434,13 @@ subroutine disturbance_rates( site_in, bc_in) endif ! Fire Disturbance Rate - currentPatch%disturbance_rates(dtype_ifire) = currentPatch%frac_burnt + currentPatch%disturbance_rates(dtype_ifire) = ( currentPatch%frac_burnt + currentPatch%rxfire_frac_burnt ) ! Fires can't burn the whole patch, as this causes /0 errors. if (currentPatch%disturbance_rates(dtype_ifire) > 0.98_r8)then - msg = 'very high fire areas'//trim(A2S(currentPatch%disturbance_rates(:)))//trim(N2S(currentPatch%frac_burnt)) + msg = 'very high fire areas'//trim(A2S(currentPatch%disturbance_rates(:)))//trim(N2S((currentPatch%frac_burnt + & + currentPatch%rxfire_frac_burnt))) call FatesWarn(msg,index=2) endif @@ -982,22 +983,40 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%fire_mort / hlm_freq_day + + currentSite%rxfmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & + currentSite%rxfmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rxfire_mort / hlm_freq_day ! for prescribed fire currentSite%fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%fmort_carbonflux_canopy(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 + currentSite%rxfmort_carbonflux_canopy(currentCohort%pft) = & + currentSite%rxfmort_carbonflux_canopy(currentCohort%pft) + & + (nc%n * currentCohort%rxfire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + else ! understory currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%fire_mort / hlm_freq_day + + currentSite%rxfmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & + currentSite%rxfmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rxfire_mort / hlm_freq_day currentSite%fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%fmort_carbonflux_ustory(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%rxfmort_carbonflux_ustory(currentCohort%pft) = & + currentSite%rxfmort_carbonflux_ustory(currentCohort%pft) + & + (nc%n * currentCohort%rxfire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 end if currentSite%fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & @@ -1006,6 +1025,13 @@ subroutine spawn_patches( currentSite, bc_in) ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & leaf_c ) * & g_per_kg * days_per_sec * ha_per_m2 + + currentSite%rxfmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & + currentSite%rxfmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & + (nc%n * currentCohort%rxfire_mort) * & + ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & + leaf_c ) * & + g_per_kg * days_per_sec * ha_per_m2 currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & @@ -1015,8 +1041,15 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%crownfire_mort / hlm_freq_day + currentSite%rxfmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & + currentSite%rxfmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rxcambial_mort / hlm_freq_day + currentSite%rxfmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & + currentSite%rxfmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rxcrownfire_mort / hlm_freq_day + ! loss of individual from fire in new patch. - nc%n = nc%n * (1.0_r8 - currentCohort%fire_mort) + nc%n = nc%n * (1.0_r8 - (currentCohort%fire_mort + currentCohort%rxfire_mort)) nc%cmort = currentCohort%cmort nc%hmort = currentCohort%hmort @@ -1049,11 +1082,14 @@ subroutine spawn_patches( currentSite, bc_in) if( (leaf_burn_frac < 0._r8) .or. & (leaf_burn_frac > 1._r8) .or. & (currentCohort%fire_mort < 0._r8) .or. & - (currentCohort%fire_mort > 1._r8)) then + (currentCohort%fire_mort > 1._r8) .or. & + (currentCohort%rxfire_mort < 0._r8) .or. & + (currentCohort%rxfire_mort > 1._r8)) then write(fates_log(),*) 'unexpected fire fractions' write(fates_log(),*) prt_params%woody(currentCohort%pft) write(fates_log(),*) leaf_burn_frac write(fates_log(),*) currentCohort%fire_mort + write(fates_log(),*) currentCohort%rxfire_mort call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -2222,14 +2258,14 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & !--------------------------------------------------------------------- ! Only do this if there was a fire in this actual patch. - if ( currentPatch%fire == ifalse ) return + if ( currentPatch%fire == ifalse .and. currentPatch%rxfire == ifalse ) return ! If plant hydraulics are turned on, account for water leaving the plant-soil ! mass balance through the dead trees if (hlm_use_planthydro == itrue) then currentCohort => currentPatch%shortest do while(associated(currentCohort)) - num_dead_trees = (currentCohort%fire_mort * & + num_dead_trees = (( currentCohort%fire_mort + currentCohort%rxfire_mort)* & currentCohort%n*patch_site_areadis/currentPatch%area) call AccumulateMortalityWaterStorage(currentSite,currentCohort,num_dead_trees) currentCohort => currentCohort%taller @@ -2303,8 +2339,8 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & ! Absolute number of dead trees being transfered in with the donated area - num_dead_trees = (currentCohort%fire_mort*currentCohort%n * & - patch_site_areadis/currentPatch%area) + num_dead_trees = ((currentCohort%fire_mort + currentCohort%rxfire_mort) * & + currentCohort%n * patch_site_areadis/currentPatch%area) ! Contribution of dead trees to leaf litter donatable_mass = num_dead_trees * (leaf_m+repro_m) * & From 335874fb7217e9ed5b92e6bc87843b2cfb0ba5ec Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 14:21:49 -0800 Subject: [PATCH 009/194] add rxfire relevant variable to cohort --- biogeochem/FatesCohortMod.F90 | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/biogeochem/FatesCohortMod.F90 b/biogeochem/FatesCohortMod.F90 index f325449cf6..1500561836 100644 --- a/biogeochem/FatesCohortMod.F90 +++ b/biogeochem/FatesCohortMod.F90 @@ -272,6 +272,9 @@ module FatesCohortMod real(r8) :: crownfire_mort ! probability of tree post-fire mortality from crown scorch [0-1] ! (conditional on the tree being subjected to the fire) real(r8) :: fire_mort ! post-fire mortality from cambial and crown damage assuming two are independent [0-1] + real(r8) :: rxcambial_mort ! cambial kill mortality due to prescribed fire + real(r8) :: rxcrownfire_mort ! crown fire mortality due to prescribed fire + real(r8) :: rxfire_mort ! post-fire mortality due to prescribed fire !--------------------------------------------------------------------------- @@ -450,6 +453,9 @@ subroutine NanValues(this) this%cambial_mort = nan this%crownfire_mort = nan this%fire_mort = nan + this%rxcambial_mort = nan + this%rxcrownfire_mort = nan + this%rxfire_mort = nan end subroutine NanValues @@ -536,6 +542,9 @@ subroutine ZeroValues(this) this%cambial_mort = 0._r8 this%crownfire_mort = 0._r8 this%fire_mort = 0._r8 + this%rxcambial_mort = 0._r8 + this%rxcrownfire_mort = 0._r8 + this%rxfire_mort = 0._r8 end subroutine ZeroValues @@ -781,6 +790,9 @@ subroutine Copy(this, copyCohort) copyCohort%cambial_mort = this%cambial_mort copyCohort%crownfire_mort = this%crownfire_mort copyCohort%fire_mort = this%fire_mort + copyCohort%rxcambial_mort = this%rxcambial_mort + copyCohort%rxcrownfire_mort = this%rxcrownfire_mort + copyCohort%rxfire_mort = this%rxfire_mort ! HYDRAULICS if (hlm_use_planthydro .eq. itrue) then @@ -1081,6 +1093,9 @@ subroutine Dump(this) write(fates_log(),*) 'cohort%fire_mort = ', this%fire_mort write(fates_log(),*) 'cohort%crownfire_mort = ', this%crownfire_mort write(fates_log(),*) 'cohort%cambial_mort = ', this%cambial_mort + write(fates_log(),*) 'cohort%rxcrownfire_mort = ', this%rxcrownfire_mort + write(fates_log(),*) 'cohort%rxcambial_mort = ', this%rxcambial_mort + write(fates_log(),*) 'cohort%rxfire_mort = ', this%rxfire_mort write(fates_log(),*) 'cohort%size_class = ', this%size_class write(fates_log(),*) 'cohort%size_by_pft_class = ', this%size_by_pft_class From f6f648a05ac21d916ab03bb971e899533a08ddca Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 14:25:02 -0800 Subject: [PATCH 010/194] add patch level prescribed fire var --- biogeochem/FatesPatchMod.F90 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index f8afc711db..0428b7e43c 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -215,6 +215,11 @@ module FatesPatchMod real(r8) :: fd ! fire duration [min] real(r8) :: frac_burnt ! fraction of patch burnt by fire + ! prescribed fire + integer :: rxfire ! is there a prescribed fire? [1=yes; 0=no]; + real(r8) :: rxfire_fi ! average fire intensity of prescribed fire flaming front + real(r8) :: rxfire_frac_burnt ! fraction burnt by prescribed fire, it's user defined at patch level per fire event + ! fire effects real(r8) :: scorch_ht(maxpft) ! scorch height [m] real(r8) :: tfc_ros ! total intensity-relevant fuel consumed - no trunks [kgC/m2 of burned ground/day] @@ -503,6 +508,9 @@ subroutine NanValues(this) this%tau_l = nan this%fi = nan this%fire = fates_unset_int + this%rxfire = fates_unset_int + this%rxfire_fi = nan + this%rxfire_frac_burnt = nan this%fd = nan this%scorch_ht(:) = nan this%tfc_ros = nan @@ -592,6 +600,8 @@ subroutine ZeroValues(this) this%scorch_ht(:) = 0.0_r8 this%tfc_ros = 0.0_r8 this%frac_burnt = 0.0_r8 + this%rxfire_fi = 0.0_r8 + this%rxfire_frac_burnt = 0.0_r8 end subroutine ZeroValues From 35e0c0984e5e0834db0a8d4a27c54883ff51db7a Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 14:48:15 -0800 Subject: [PATCH 011/194] add prescribed fire parameters to SFparams --- fire/SFParamsMod.F90 | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/fire/SFParamsMod.F90 b/fire/SFParamsMod.F90 index 65d87e5c6d..78af5a85c9 100644 --- a/fire/SFParamsMod.F90 +++ b/fire/SFParamsMod.F90 @@ -37,6 +37,18 @@ module SFParamsMod real(r8),protected, public :: SF_val_low_moisture_Slope(num_fuel_classes) real(r8),protected, public :: SF_val_mid_moisture_Coeff(num_fuel_classes) real(r8),protected, public :: SF_val_mid_moisture_Slope(num_fuel_classes) + ! Prescribed fire relevant parameters + real(r8),protected, public :: SF_val_rxfire_tpup ! temprature upper threshold for conducting RX fire + real(r8),protected, public :: SF_val_rxfire_tplw ! temprature lower threshold + real(r8),protected, public :: SF_val_rxfire_rhup ! relative humidity upper threshold + real(r8),protected, public :: SF_val_rxfire_rhlw ! relative humidity lower threshold + real(r8),protected, public :: SF_val_rxfire_wdup ! wind speed upper threshold + real(r8),protected, public :: SF_val_rxfire_wdlw ! wind speed lower threshold + real(r8),protected, public :: SF_val_rxfire_AB ! prescribed fire burned fraction per day + real(r8),protected, public :: SF_val_rxfire_minthreshold ! minimum fire energy of rx fire, for management outcomes really + real(r8),protected, public :: SF_val_rxfire_maxthreshold ! maximum fire energy + real(r8),protected, public :: SF_val_rxfire_fuel_min ! minimum fuel load at the patch for the need of rx fire management + real(r8),protected, public :: SF_val_rxfire_fuel_max ! maximum fuel load, above which might be risky for conducting rx fire character(len=param_string_length),parameter :: SF_name_fdi_alpha = "fates_fire_fdi_alpha" character(len=param_string_length),parameter :: SF_name_miner_total = "fates_fire_miner_total" @@ -57,6 +69,18 @@ module SFParamsMod character(len=param_string_length),parameter :: SF_name_low_moisture_Slope = "fates_fire_low_moisture_Slope" character(len=param_string_length),parameter :: SF_name_mid_moisture_Coeff = "fates_fire_mid_moisture_Coeff" character(len=param_string_length),parameter :: SF_name_mid_moisture_Slope = "fates_fire_mid_moisture_Slope" + character(len=param_string_length),parameter :: SF_name_rxfire_tpup = "fates_rxfire_temp_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_tplw = "fates_rxfire_temp_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_rhup = "fates_rxfire_rh_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_rhlw = "fates_rxfire_rh_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_wdup = "fates_rxfire_wind_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_wdlw = "fates_rxfire_wind_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_AB = "fates_rxfire_AB" + character(len=param_string_length),parameter :: SF_name_rxfire_min_threshold = "fates_rxfire_min_threshold" + character(len=param_string_length),parameter :: SF_name_rxfire_max_threshold = "fates_rxfire_max_threshold" + character(len=param_string_length),parameter :: SF_name_rxfire_fuel_min = "fates_rxfire_fuel_min" + character(len=param_string_length),parameter :: SF_name_rxfire_fuel_max = "fates_rxfire_fuel_max" + character(len=*), parameter, private :: sourcefile = __FILE__ real(r8), parameter, private :: min_fire_threshold = 0.0001_r8 ! The minimum reasonable fire intensity threshold [kW/m] @@ -157,6 +181,17 @@ subroutine SpitFireParamsInit() SF_val_low_moisture_Slope(:) = nan SF_val_mid_moisture_Coeff(:) = nan SF_val_mid_moisture_Slope(:) = nan + SF_val_rxfire_tpup = nan + SF_val_rxfire_tplw = nan + SF_val_rxfire_rhup = nan + SF_val_rxfire_rhlw = nan + SF_val_rxfire_wdup = nan + SF_val_rxfire_wdlw = nan + SF_val_rxfire_AB = nan + SF_val_rxfire_minthreshold = nan + SF_val_rxfire_maxthreshold = nan + SF_val_rxfire_fuel_min = nan + SF_val_rxfire_fuel_max = nan end subroutine SpitFireParamsInit @@ -228,6 +263,40 @@ subroutine SpitFireRegisterScalars(fates_params) call fates_params%RegisterParameter(name=SF_name_fire_threshold, dimension_shape=dimension_shape_scalar, & dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_tpup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_tplw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_rhup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_rhlw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_wdup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_wdlw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_AB, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_min_threshold, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_max_threshold, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_fuel_min, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_fuel_max, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + end subroutine SpitFireRegisterScalars @@ -267,6 +336,39 @@ subroutine SpitFireReceiveScalars(fates_params) call fates_params%RetrieveParameter(name=SF_name_fire_threshold, & data=SF_val_fire_threshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_tpup, & + data=SF_val_rxfire_tpup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_tplw, & + data=SF_val_rxfire_tplw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_rhup, & + data=SF_val_rxfire_rhup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_rhlw, & + data=SF_val_rxfire_rhlw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_wdup, & + data=SF_val_rxfire_wdup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_wdlw, & + data=SF_val_rxfire_wdlw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_AB, & + data=SF_val_rxfire_AB) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_min_threshold, & + data=SF_val_rxfire_minthreshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_max_threshold, & + data=SF_val_rxfire_maxthreshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_min, & + data=SF_val_rxfire_fuel_min) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_max, & + data=SF_val_rxfire_fuel_max) From 0ce009f373d4202725050b2e1a0673c3e2085a97 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 14:48:48 -0800 Subject: [PATCH 012/194] add rxfire relevant params in param file --- parameter_files/fates_params_default.cdl | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 1f77e53e11..6c7dd58a7c 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -900,6 +900,42 @@ variables: double fates_regeneration_model ; fates_regeneration_model:units = "-" ; fates_regeneration_model:long_name = "switch for choosing between FATES\'s: 1) default regeneration scheme , 2) the Tree Recruitment Scheme (Hanbury-Brown et al., 2022), or (3) the Tree Recruitment Scheme without seedling dynamics" ; + double fates_rxfire_switch ; + fates_rxfire_switch:units = "unitless" ; + fates_rxfire_switch:long_name = "management fire mode, 1 = use management fire, 0 = turn off management fire" ; + double fates_rxfire_temp_upthreshold ; + fates_rxfire_temp_upthreshold:units = "degree C"; + fates_rxfire_temp_upthreshold:long_name= "maximum temprature threshold for conducting prescribed fire"; + double fates_rxfire_temp_lwthreshold ; + fates_rxfire_temp_lwthreshold:units = "degree C"; + fates_rxfire_temp_lwthreshold:long_name= "minimum temprature threshold for conducting prescribed fire"; + double fates_rxfire_rh_upthreshold ; + fates_rxfire_rh_upthreshold:units = "%"; + fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold for conducting prescribeb fire"; + double fates_rxfire_rh_lwthreshold ; + fates_rxfire_rh_lwthreshold:units = "%"; + fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold for conducting prescribeb fire"; + double fates_rxfire_wind_upthreshold ; + fates_rxfire_wind_upthreshold:units = "m/s"; + fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold for conducting prescribeb fire"; + double fates_rxfire_wind_lwthreshold ; + fates_rxfire_wind_lwthreshold:units = "m/s"; + fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold for conducting prescribeb fire"; + double fates_rxfire_AB ; + fates_rxfire_AB:units = "m2/day"; + fates_rxfire_AB:long_name= "daily burn capacity of prescribed fire"; + double fates_rxfire_min_threshold ; + fates_rxfire_min_threshold:units = "kJ/m/s or kW/s"; + fates_rxfire_min_threshold:long_name= "minimum energy threshold for conducting prescribeb fire"; + double fates_rxfire_max_threshold ; + fates_rxfire_max_threshold:units = "kJ/m/s or kW/s"; + fates_rxfire_max_threshold:long_name= "maximum energy threshold for conducting prescribeb fire"; + double fates_rxfire_fuel_min ; + fates_rxfire_fuel_min:units = "kgC/m2"; + fates_rxfire_fuel_min:long_name= "minimum fuel load at the patch level for prescribed fire to occur"; + double fates_rxfire_fuel_max ; + fates_rxfire_fuel_max:units = "kgC/m2"; + fates_rxfire_fuel_max:long_name= "maximum fuel load above which prescribed fire can be risky"; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; @@ -1837,6 +1873,30 @@ data: fates_regeneration_model = 1 ; + fates_rxfire_switch = 0 ; + + fates_rxfire_temp_upthreshold = 30 ; + + fates_rxfire_temp_lwthreshold = 5 ; + + fates_rxfire_rh_upthreshold = 55 ; + + fates_rxfire_rh_lwthreshold = 30 ; + + fates_rxfire_wind_upthreshold = 10 ; + + fates_rxfire_wind_lwthreshold = 2 ; + + fates_rxfire_AB = 0.01 ; + + fates_rxfire_min_threshold = 50 ; + + fates_rxfire_max_threshold = 500 ; + + fates_rxfire_fuel_min = 0.5 ; + + fates_rxfire_fuel_max = 1.5 ; + fates_soil_salinity = 0.4 ; fates_trs_seedling2sap_par_timescale = 32 ; From 521b2243399dc862130d39b1d1a64e172cf9f151 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 15:10:45 -0800 Subject: [PATCH 013/194] add prescribed fire mortality and other var for site level tracking --- main/EDInitMod.F90 | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 9fc11491c1..b2e8acd8a4 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -150,6 +150,10 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_ustory(1:nlevsclass,1:numpft)) allocate(site_in%fmort_rate_cambial(1:nlevsclass,1:numpft)) allocate(site_in%fmort_rate_crown(1:nlevsclass,1:numpft)) + allocate(site_in%rxfmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%rxfmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%rxfmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%rxfmort_rate_crown(nlevsclass,1:numpft)) allocate(site_in%growthflux_fusion(1:nlevsclass,1:numpft)) allocate(site_in%mass_balance(1:num_elements)) allocate(site_in%iflux_balance(1:num_elements)) @@ -165,6 +169,11 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) allocate(site_in%fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rxfmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rxfmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rxfmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rxfmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) else allocate(site_in%term_nindivs_canopy_damage(1,1,1)) allocate(site_in%term_nindivs_ustory_damage(1,1,1)) @@ -176,6 +185,10 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_ustory_damage(1,1,1)) allocate(site_in%fmort_cflux_canopy_damage(1,1)) allocate(site_in%fmort_cflux_ustory_damage(1,1)) + allocate(site_in%rxfmort_rate_canopy_damage(1,1,1)) + allocate(site_in%rxfmort_rate_ustory_damage(1,1,1)) + allocate(site_in%rxfmort_cflux_canopy_damage(1,1)) + allocate(site_in%rxfmort_cflux_ustory_damage(1,1)) end if allocate(site_in%term_carbonflux_canopy(1:n_term_mort_types,1:numpft)) @@ -183,10 +196,14 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_carbonflux(1:numpft)) allocate(site_in%fmort_carbonflux_canopy(1:numpft)) allocate(site_in%fmort_carbonflux_ustory(1:numpft)) + allocate(site_in%rxfmort_carbonflux_canopy(1:numpft)) + allocate(site_in%rxfmort_carbonflux_ustory(1:numpft)) allocate(site_in%term_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%imort_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%fmort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%rxfmort_abg_flux(1:nlevsclass,1:numpft)) + site_in%nlevsoil = bc_in%nlevsoil allocate(site_in%rootfrac_scr(site_in%nlevsoil)) @@ -315,6 +332,8 @@ subroutine zero_site( site_in ) site_in%imort_crownarea = 0._r8 site_in%fmort_crownarea_canopy = 0._r8 site_in%fmort_crownarea_ustory = 0._r8 + site_in%rxfmort_crownarea_canopy = 0._r8 + site_in%rxfmort_crownarea_ustory = 0._r8 site_in%term_carbonflux_canopy(:,:) = 0._r8 site_in%term_carbonflux_ustory(:,:) = 0._r8 site_in%recruitment_rate(:) = 0._r8 @@ -326,9 +345,16 @@ subroutine zero_site( site_in ) site_in%fmort_carbonflux_ustory(:) = 0._r8 site_in%fmort_rate_cambial(:,:) = 0._r8 site_in%fmort_rate_crown(:,:) = 0._r8 + site_in%rxfmort_rate_canopy(:,:) = 0._r8 + site_in%rxfmort_rate_ustory(:,:) = 0._r8 + site_in%rxfmort_carbonflux_ustory(:) = 0._r8 + site_in%rxfmort_carbonflux_canopy(:) = 0._r8 + site_in%rxfmort_rate_cambial(:,:) = 0._r8 + site_in%rxfmort_rate_crown(:,:) = 0._r8 site_in%term_abg_flux(:,:) = 0._r8 site_in%imort_abg_flux(:,:) = 0._r8 site_in%fmort_abg_flux(:,:) = 0._r8 + site_in%rxfmort_abg_flux(:,:) = 0._r8 ! fusoin-induced growth flux of individuals site_in%growthflux_fusion(:,:) = 0._r8 @@ -352,6 +378,10 @@ subroutine zero_site( site_in ) site_in%fmort_rate_ustory_damage(:,:,:) = 0._r8 site_in%fmort_cflux_canopy_damage(:,:) = 0._r8 site_in%fmort_cflux_ustory_damage(:,:) = 0._r8 + site_in%rxfmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%rxfmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%rxfmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%rxfmort_cflux_ustory_damage(:,:) = 0._r8 ! Resources management (logging/harvesting, etc) site_in%resources_management%harvest_debt = 0.0_r8 @@ -1007,6 +1037,10 @@ subroutine init_patches( nsites, sites, bc_in) currentPatch%ros_back = 0._r8 currentPatch%scorch_ht(:) = 0._r8 currentPatch%frac_burnt = 0._r8 + currentPatch%rxfire = 0 + currentPatch%rxfire_fi = 0._r8 + currentPatch%rxfire_frac_burnt = 0._r8 + currentPatch => currentPatch%older enddo enddo From 9b7820441cdd9a91cd97f0e8ae80cee75d3733ec Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Wed, 5 Mar 2025 15:23:08 -0800 Subject: [PATCH 014/194] add rxfire switch --- main/EDParamsMod.F90 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/main/EDParamsMod.F90 b/main/EDParamsMod.F90 index cc906fecef..3314f0a25a 100644 --- a/main/EDParamsMod.F90 +++ b/main/EDParamsMod.F90 @@ -51,7 +51,6 @@ module EDParamsMod ! 1=non-acclimating, 2=Kumarathunge et al., 2019 integer,protected, public :: radiation_model ! Switch betrween Norman (1) and Two-stream (2) radiation models - integer,protected, public :: mort_cstarvation_model ! Switch for carbon starvation mortality: ! 1 -- Linear model ! 2 -- Exponential model @@ -83,6 +82,7 @@ module EDParamsMod ! (2) for the Tree Recruitment Scheme (Hanbury-Brown et al., 2022) ! (3) for the Tree Recruitment Scheme without seedling dynamics + logical,protected, public :: rxfire_switch ! switch between 1=use management fire and 0=no management fire logical,protected, public :: active_crown_fire ! flag, 1=active crown fire 0=no active crown fire character(len=param_string_length),parameter :: fates_name_active_crown_fire = "fates_fire_active_crown_fire" @@ -172,6 +172,7 @@ module EDParamsMod character(len=param_string_length),parameter,public :: ED_name_stomatal_model= "fates_leaf_stomatal_model" character(len=param_string_length),parameter,public :: ED_name_dayl_switch= "fates_daylength_factor_switch" character(len=param_string_length),parameter,public :: ED_name_regeneration_model= "fates_regeneration_model" + character(len=param_string_length),parameter,public :: fates_name_rxfire_switch= "fates_rxfire_switch" character(len=param_string_length),parameter,public :: name_theta_cj_c3 = "fates_leaf_theta_cj_c3" character(len=param_string_length),parameter,public :: name_theta_cj_c4 = "fates_leaf_theta_cj_c4" @@ -505,6 +506,9 @@ subroutine FatesRegisterParams(fates_params) call fates_params%RegisterParameter(name=ED_name_regeneration_model, dimension_shape=dimension_shape_scalar, & dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=fates_name_rxfire_switch, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) call fates_params%RegisterParameter(name=stomatal_assim_name, dimension_shape=dimension_shape_scalar, & dimension_names=dim_names_scalar) @@ -734,6 +738,10 @@ subroutine FatesReceiveParams(fates_params) call fates_params%RetrieveParameter(name=ED_name_regeneration_model, & data=tmpreal) regeneration_model = nint(tmpreal) + + call fates_params%RetrieveParameter(name=fates_name_rxfire_switch, & + data=tmpreal) + rxfire_switch = (abs(tmpreal-1.0_r8) Date: Wed, 5 Mar 2025 20:06:18 -0800 Subject: [PATCH 017/194] add prescribed fire relevant to restart hist --- main/FatesRestartInterfaceMod.F90 | 152 +++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 11 deletions(-) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index bf63274d0e..ea50f0bae8 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -241,9 +241,13 @@ module FatesRestartInterfaceMod integer :: ir_area_pft_sift integer :: ir_fmortrate_cano_siscpf integer :: ir_fmortrate_usto_siscpf + integer :: ir_rxfmortrate_cano_siscpf + integer :: ir_rxfmortrate_usto_siscpf integer :: ir_imortrate_siscpf integer :: ir_fmortrate_crown_siscpf integer :: ir_fmortrate_cambi_siscpf + integer :: ir_rxfmortrate_crown_siscpf + integer :: ir_rxfmortrate_cambi_siscpf integer :: ir_termnindiv_cano_siscpf integer :: ir_termnindiv_usto_siscpf integer :: ir_growflx_fusion_siscpf @@ -255,6 +259,8 @@ module FatesRestartInterfaceMod integer :: ir_imortcarea_si integer :: ir_fmortcarea_cano_si integer :: ir_fmortcarea_usto_si + integer :: ir_rxfmortcarea_cano_si + integer :: ir_rxfmortcarea_usto_si integer :: ir_termcflux_cano_sipft integer :: ir_termcflux_usto_sipft integer :: ir_democflux_si @@ -262,9 +268,12 @@ module FatesRestartInterfaceMod integer :: ir_imortcflux_sipft integer :: ir_fmortcflux_cano_sipft integer :: ir_fmortcflux_usto_sipft + integer :: ir_rxfmortcflux_cano_sipft + integer :: ir_rxfmortcflux_usto_sipft integer :: ir_abg_term_flux_siscpf integer :: ir_abg_imort_flux_siscpf integer :: ir_abg_fmort_flux_siscpf + integer :: ir_abg_rxfmort_flux_siscpf integer :: ir_disturbance_rates_siluludi @@ -292,11 +301,15 @@ module FatesRestartInterfaceMod integer :: ir_termnindiv_usto_sicdpf integer :: ir_fmortrate_cano_sicdpf integer :: ir_fmortrate_usto_sicdpf + integer :: ir_rxfmortrate_cano_sicdpf + integer :: ir_rxfmortrate_usto_sicdpf integer :: ir_imortcflux_sicdsc integer :: ir_termcflux_cano_sicdsc integer :: ir_termcflux_usto_sicdsc integer :: ir_fmortcflux_cano_sicdsc integer :: ir_fmortcflux_usto_sicdsc + integer :: ir_rxfmortcflux_cano_sicdsc + integer :: ir_rxfmortcflux_usto_sicdsc integer :: ir_crownarea_cano_si integer :: ir_crownarea_usto_si integer :: ir_emanpp_si @@ -1385,6 +1398,16 @@ subroutine define_restart_vars(this, initialize_variables) long_name='fates diagnostics on fire mortality ustory', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_siscpf) + + call this%set_restart_var(vname='fates_rxfmortrate_canopy', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality canopy', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cano_siscpf) + + call this%set_restart_var(vname='fates_rxfmortrate_ustory', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality ustory', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_usto_siscpf) call this%set_restart_var(vname='fates_imortrate', vtype=cohort_r8, & long_name='fates diagnostics on impact mortality', & @@ -1401,6 +1424,16 @@ subroutine define_restart_vars(this, initialize_variables) units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cambi_siscpf) + call this%set_restart_var(vname='fates_rxfmortrate_crown', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire crown fire mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_crown_siscpf) + + call this%set_restart_var(vname='fates_rxfmortrate_cambi', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire cambial mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cambi_siscpf) + call this%set_restart_var(vname='fates_termn_canopy', vtype=cohort_r8, & long_name='fates diagnostics on termin mortality canopy', & units='indiv/ha/day', flushval = flushzero, & @@ -1431,12 +1464,12 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcflux_sipft) - call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & + call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & long_name='crownarea of indivs killed due to impact mort', & units='m2/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcarea_si) - call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic biomass of canopy fire', & units='gC/m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sipft) @@ -1446,42 +1479,57 @@ subroutine define_restart_vars(this, initialize_variables) units='gC/m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sipft) + call this%set_restart_var(vname='fates_rxfmortcflux_canopy', vtype=cohort_r8, & + long_name='fates diagnostic biomass of canopy rx fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_cano_sipft) + + call this%set_restart_var(vname='fates_rxfmortcflux_ustory', vtype=cohort_r8, & + long_name='fates diagnostic biomass of understory rx fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_usto_sipft) + call this%set_restart_var(vname='fates_termcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux canopy', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_cano_sipft ) - call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux understory', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_usto_sipft ) - call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from termination mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_term_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from impact mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_imort_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from fire mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_fmort_flux_siscpf ) - call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_abg_rxfmort_flux', vtype=cohort_r8, & + long_name='fates aboveground biomass loss from rx fire mortality', & + units='', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_rxfmort_flux_siscpf ) + + call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & long_name='fates diagnostic demotion carbon flux', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_democflux_si ) - call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & long_name='fates diagnostic promotion carbon flux ', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_promcflux_si ) - call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & + call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & long_name='fates diagnostic crownarea of canopy fire', & units='m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_cano_si) @@ -1491,6 +1539,16 @@ subroutine define_restart_vars(this, initialize_variables) units='m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_usto_si) + call this%set_restart_var(vname='fates_rxfmortcarea_canopy', vtype=site_r8, & + long_name='fates diagnostic crownarea of canopy rx fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcarea_cano_si) + + call this%set_restart_var(vname='fates_rxfmortcarea_ustory', vtype=site_r8, & + long_name='fates diagnostic crownarea of understory rx fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcarea_usto_si) + call this%set_restart_var(vname='fates_termcarea_canopy', vtype=site_r8, & long_name='fates diagnostic term crownarea canopy', & units='', flushval = flushzero, & @@ -1527,6 +1585,16 @@ subroutine define_restart_vars(this, initialize_variables) units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_sicdpf) + call this%set_restart_var(vname='fates_rxfmortrate_cano_dam', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cano_sicdpf) + + call this%set_restart_var(vname='fates_rxfmortrate_usto_dam', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_usto_sicdpf) + call this%set_restart_var(vname='fates_imortcflux_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to impact mort by damage class', & units='kgC/ha/day', flushval = flushzero, & @@ -1552,6 +1620,16 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_rxfmortcflux_cano_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to rx fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_cano_sicdsc) + + call this%set_restart_var(vname='fates_rxfmortcflux_usto_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to rx fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_crownarea_canopy_damage', vtype=site_r8, & long_name='fates area lost from damage each year', & units='m2/ha/year', flushval = flushzero, & @@ -2174,9 +2252,13 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & + rio_rxfmortrate_cano_siscpf => this%rvars(ir_rxfmortrate_cano_siscpf)%r81d, & + rio_rxfmortrate_usto_siscpf => this%rvars(ir_rxfmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & + rio_rxfmortrate_crown_siscpf => this%rvars(ir_rxfmortrate_crown_siscpf)%r81d, & + rio_rxfmortrate_cambi_siscpf => this%rvars(ir_rxfmortrate_cambi_siscpf)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & rio_growflx_fusion_siscpf => this%rvars(ir_growflx_fusion_siscpf)%r81d, & @@ -2188,6 +2270,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & + rio_rxfmortcarea_cano_si => this%rvars(ir_rxfmortcarea_cano_si)%r81d, & + rio_rxfmortcarea_usto_si => this%rvars(ir_rxfmortcarea_usto_si)%r81d, & rio_termcflux_cano_sipft => this%rvars(ir_termcflux_cano_sipft)%r81d, & rio_termcflux_usto_sipft => this%rvars(ir_termcflux_usto_sipft)%r81d, & rio_democflux_si => this%rvars(ir_democflux_si)%r81d, & @@ -2195,8 +2279,11 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & + rio_rxfmortcflux_cano_sipft => this%rvars(ir_rxfmortcflux_cano_sipft)%r81d, & + rio_rxfmortcflux_usto_sipft => this%rvars(ir_rxfmortcflux_usto_sipft)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & + rio_abg_rxfmort_flux_siscpf => this%rvars(ir_abg_rxfmort_flux_siscpf)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_landuse_config_si => this%rvars(ir_landuse_config_si)%int1d, & @@ -2211,6 +2298,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & + rio_rxfmortrate_cano_sicdpf => this%rvars(ir_rxfmortrate_cano_sicdpf)%r81d, & + rio_rxfmortrate_usto_sicdpf => this%rvars(ir_rxfmortrate_usto_sicdpf)%r81d, & + rio_rxfmortcflux_cano_sicdsc => this%rvars(ir_rxfmortcflux_cano_sicdsc)%r81d, & + rio_rxfmortcflux_usto_sicdsc => this%rvars(ir_rxfmortcflux_usto_sicdsc)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d) @@ -2289,11 +2380,16 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortrate_siscpf(io_idx_si_scpf) = sites(s)%imort_rate(i_scls, i_pft) rio_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_crown(i_scls, i_pft) rio_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_cambial(i_scls, i_pft) + rio_rxfmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_canopy(i_scls, i_pft) + rio_rxfmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_ustory(i_scls, i_pft) + rio_rxfmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_crown(i_scls, i_pft) + rio_rxfmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_cambial(i_scls, i_pft) rio_growflx_fusion_siscpf(io_idx_si_scpf) = sites(s)%growthflux_fusion(i_scls, i_pft) rio_abg_term_flux_siscpf(io_idx_si_scpf) = sites(s)%term_abg_flux(i_scls, i_pft) rio_abg_imort_flux_siscpf(io_idx_si_scpf) = sites(s)%imort_abg_flux(i_scls, i_pft) rio_abg_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%fmort_abg_flux(i_scls, i_pft) - io_idx_si_scpf = io_idx_si_scpf + 1 + rio_abg_rxfmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_abg_flux(i_scls, i_pft) + rio_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) rio_termnindiv_usto_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_ustory(i_term_type,i_scls,i_pft) @@ -2310,6 +2406,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) end do rio_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_canopy(i_pft) rio_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_ustory(i_pft) + rio_rxfmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rxfmort_carbonflux_canopy(i_pft) + rio_rxfmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%rxfmort_carbonflux_ustory(i_pft) rio_imortcflux_sipft(io_idx_si_pft) = sites(s)%imort_carbonflux(i_pft) rio_dd_status_sift(io_idx_si_pft) = sites(s)%dstatus(i_pft) rio_dleafondate_sift(io_idx_si_pft) = sites(s)%dleafondate(i_pft) @@ -2700,6 +2798,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) + rio_rxfmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%rxfmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_rxfmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%rxfmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_rxfmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%rxfmort_cflux_canopy_damage(i_cdam, i_scls) + rio_rxfmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%rxfmort_cflux_ustory_damage(i_cdam, i_scls) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -2718,6 +2820,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcarea_si(io_idx_si) = sites(s)%imort_crownarea rio_fmortcarea_cano_si(io_idx_si) = sites(s)%fmort_crownarea_canopy rio_fmortcarea_usto_si(io_idx_si) = sites(s)%fmort_crownarea_ustory + rio_rxfmortcarea_cano_si(io_idx_si) = sites(s)%rxfmort_crownarea_canopy + rio_rxfmortcarea_usto_si(io_idx_si) = sites(s)%rxfmort_crownarea_ustory rio_cd_status_si(io_idx_si) = sites(s)%cstatus rio_nchill_days_si(io_idx_si) = sites(s)%nchilldays @@ -3177,9 +3281,13 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & + rio_rxfmortrate_cano_siscpf => this%rvars(ir_rxfmortrate_cano_siscpf)%r81d, & + rio_rxfmortrate_usto_siscpf => this%rvars(ir_rxfmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & + rio_rxfmortrate_crown_siscpf => this%rvars(ir_rxfmortrate_crown_siscpf)%r81d, & + rio_rxfmortrate_cambi_siscpf => this%rvars(ir_rxfmortrate_cambi_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & @@ -3195,6 +3303,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & + rio_rxfmortcarea_cano_si => this%rvars(ir_rxfmortcarea_cano_si)%r81d, & + rio_rxfmortcarea_usto_si => this%rvars(ir_rxfmortcarea_usto_si)%r81d, & rio_imortrate_sicdpf => this%rvars(ir_imortrate_sicdpf)%r81d, & rio_termnindiv_cano_sicdpf => this%rvars(ir_termnindiv_cano_sicdpf)%r81d, & rio_termnindiv_usto_sicdpf => this%rvars(ir_termnindiv_usto_sicdpf)%r81d, & @@ -3205,6 +3315,12 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & + rio_rxfmortrate_cano_sicdpf => this%rvars(ir_rxfmortrate_cano_sicdpf)%r81d, & + rio_rxfmortrate_usto_sicdpf => this%rvars(ir_rxfmortrate_usto_sicdpf)%r81d, & + rio_rxfmortcflux_cano_sicdsc => this%rvars(ir_rxfmortcflux_cano_sicdsc)%r81d, & + rio_rxfmortcflux_usto_sicdsc => this%rvars(ir_rxfmortcflux_usto_sicdsc)%r81d, & + rio_rxfmortcflux_cano_sipft => this%rvars(ir_rxfmortcflux_cano_sipft)%r81d, & + rio_rxfmortcflux_usto_sipft => this%rvars(ir_rxfmortcflux_usto_sipft)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d, & @@ -3213,7 +3329,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & - rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d ) + rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & + rio_abg_rxfmort_flux_siscpf => this%rvars(ir_abg_rxfmort_flux_siscpf)%r81d ) totalcohorts = 0 @@ -3276,13 +3393,18 @@ subroutine get_restart_vectors(this, nc, nsites, sites) do i_pft = 1, numpft sites(s)%fmort_rate_canopy(i_scls, i_pft) = rio_fmortrate_cano_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_ustory(i_scls, i_pft) = rio_fmortrate_usto_siscpf(io_idx_si_scpf) + sites(s)%rxfmort_rate_canopy(i_scls, i_pft) = rio_rxfmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%rxfmort_rate_ustory(i_scls, i_pft) = rio_rxfmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%imort_rate(i_scls, i_pft) = rio_imortrate_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_crown(i_scls, i_pft) = rio_fmortrate_crown_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_cambial(i_scls, i_pft) = rio_fmortrate_cambi_siscpf(io_idx_si_scpf) + sites(s)%rxfmort_rate_crown(i_scls, i_pft) = rio_rxfmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%rxfmort_rate_cambial(i_scls, i_pft) = rio_rxfmortrate_cambi_siscpf(io_idx_si_scpf) sites(s)%growthflux_fusion(i_scls, i_pft) = rio_growflx_fusion_siscpf(io_idx_si_scpf) sites(s)%term_abg_flux(i_scls,i_pft) = rio_abg_term_flux_siscpf(io_idx_si_scpf) sites(s)%imort_abg_flux(i_scls,i_pft) = rio_abg_imort_flux_siscpf(io_idx_si_scpf) sites(s)%fmort_abg_flux(i_scls,i_pft) = rio_abg_fmort_flux_siscpf(io_idx_si_scpf) + sites(s)%rxfmort_abg_flux(i_scls,i_pft) = rio_abg_rxfmort_flux_siscpf(io_idx_si_scpf) io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) = rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) @@ -3300,6 +3422,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) end do sites(s)%fmort_carbonflux_canopy(i_pft) = rio_fmortcflux_cano_sipft(io_idx_si_pft) sites(s)%fmort_carbonflux_ustory(i_pft) = rio_fmortcflux_usto_sipft(io_idx_si_pft) + sites(s)%rxfmort_carbonflux_canopy(i_pft) = rio_rxfmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%rxfmort_carbonflux_ustory(i_pft) = rio_rxfmortcflux_usto_sipft(io_idx_si_pft) sites(s)%imort_carbonflux(i_pft) = rio_imortcflux_sipft(io_idx_si_pft) sites(s)%dstatus(i_pft) = rio_dd_status_sift(io_idx_si_pft) sites(s)%dleafondate(i_pft) = rio_dleafondate_sift(io_idx_si_pft) @@ -3728,6 +3852,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%rxfmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_rxfmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%rxfmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_rxfmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%rxfmort_cflux_canopy_damage(i_cdam, i_scls) = rio_rxfmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%rxfmort_cflux_ustory_damage(i_cdam, i_scls) = rio_rxfmortcflux_usto_sicdsc(io_idx_si_cdsc) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -3746,6 +3874,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%imort_crownarea = rio_imortcarea_si(io_idx_si) sites(s)%fmort_crownarea_canopy = rio_fmortcarea_cano_si(io_idx_si) sites(s)%fmort_crownarea_ustory = rio_fmortcarea_usto_si(io_idx_si) + sites(s)%rxfmort_crownarea_canopy = rio_rxfmortcarea_cano_si(io_idx_si) + sites(s)%rxfmort_crownarea_ustory = rio_rxfmortcarea_usto_si(io_idx_si) sites(s)%demotion_carbonflux = rio_democflux_si(io_idx_si) sites(s)%promotion_carbonflux = rio_promcflux_si(io_idx_si) From ddc5bbb52920a4a3b3c5892ae20e3b762eb414fa Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 6 Mar 2025 10:20:39 -0800 Subject: [PATCH 018/194] bug fix --- fire/SFMainMod.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 3129061b90..7705aa8073 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -581,6 +581,8 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) currentPatch => currentPatch%younger; end do ! end patch loop + end subroutine CalculateRxfireAreaBurnt + !--------------------------------------------------------------------------------------- From 28a1f5a82a226ad0b100437c534fabbec3a44852 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 6 Mar 2025 10:29:34 -0800 Subject: [PATCH 019/194] fix typo --- fire/SFMainMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 7705aa8073..ff22f102c2 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -798,7 +798,7 @@ subroutine post_fire_mortality ( currentSite ) currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 currentCohort%rxfire_mort = 0.0_r8 - currentCohort%rxcrownire_mort = 0.0_r8 + currentCohort%rxcrownfire_mort = 0.0_r8 currentCohort%rxcambial_mort = 0.0_r8 if ( prt_params%woody(currentCohort%pft) == itrue) then ! Equation 22 in Thonicke et al. 2010. @@ -813,7 +813,7 @@ subroutine post_fire_mortality ( currentSite ) ! now decide which type of post-fire mortality, prescribed fire or wildfire? if (currentPatch%rxfire == itrue .and. currentPatch%fire == ifalse) then currentCohort%rxfire_mort = currentCohort%fire_mort - currentCohort%rxcrownire_mort = currentCohort%crownfire_mort + currentCohort%rxcrownfire_mort = currentCohort%crownfire_mort currentCohort%rxcambial_mort = currentCohort%cambial_mort currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 From de9bff6e4a52ed2e100624535109e639536575cc Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 6 Mar 2025 10:40:32 -0800 Subject: [PATCH 020/194] typo --- main/FatesRestartInterfaceMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index ea50f0bae8..18eb7f46de 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -2389,7 +2389,7 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_abg_imort_flux_siscpf(io_idx_si_scpf) = sites(s)%imort_abg_flux(i_scls, i_pft) rio_abg_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%fmort_abg_flux(i_scls, i_pft) rio_abg_rxfmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_abg_flux(i_scls, i_pft) - rio_idx_si_scpf = io_idx_si_scpf + 1 + io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) rio_termnindiv_usto_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_ustory(i_term_type,i_scls,i_pft) From 1c17f3d2e846af0a56d5a8999425ff1a8fdd23e5 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 6 Mar 2025 12:08:53 -0800 Subject: [PATCH 021/194] add rxfire burnt fraction to patch fusion process --- biogeochem/EDPatchDynamicsMod.F90 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 4ae30c4108..50fbc303ae 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -2104,7 +2104,8 @@ subroutine TransLitterNewPatch(currentSite, & do c = 1,ncwd frac_burnt = 0.0_r8 - if (dist_type == dtype_ifire .and. currentPatch%fire == 1) then + if (dist_type == dtype_ifire .and. (currentPatch%fire == 1 .or. & + currentPatch%rxfire == 1)) then frac_burnt = currentPatch%fuel%frac_burnt(c) end if @@ -2131,7 +2132,8 @@ subroutine TransLitterNewPatch(currentSite, & enddo frac_burnt = 0.0_r8 - if (dist_type == dtype_ifire .and. currentPatch%fire == 1) then + if (dist_type == dtype_ifire .and. (currentPatch%fire == 1 .or. & + currentPatch%rxfire == 1)) then frac_burnt = currentPatch%fuel%frac_burnt(fuel_classes%dead_leaves()) end if @@ -3340,6 +3342,7 @@ subroutine fuse_2_patches(csite, dp, rp) rp%ros_back = (dp%ros_back*dp%area + rp%ros_back*rp%area) * inv_sum_area rp%scorch_ht(:) = (dp%scorch_ht(:)*dp%area + rp%scorch_ht(:)*rp%area) * inv_sum_area rp%frac_burnt = (dp%frac_burnt*dp%area + rp%frac_burnt*rp%area) * inv_sum_area + rp%rxfire_frac_burnt = (dp%rxfire_frac_burnt*dp%area + rp%rxfire_frac_burnt*rp%area) * inv_sum_area rp%btran_ft(:) = (dp%btran_ft(:)*dp%area + rp%btran_ft(:)*rp%area) * inv_sum_area rp%zstar = (dp%zstar*dp%area + rp%zstar*rp%area) * inv_sum_area rp%c_stomata = (dp%c_stomata*dp%area + rp%c_stomata*rp%area) * inv_sum_area From 6718800287be339f0ea118361b02f50e7c8cf728 Mon Sep 17 00:00:00 2001 From: Bharat Sharma Date: Fri, 7 Mar 2025 18:13:05 -0500 Subject: [PATCH 022/194] PR for dynamic L2FR --- biogeochem/EDPhysiologyMod.F90 | 326 ++++++++++---------- parteh/PRTAllometricCNPMod.F90 | 537 +++++++++++++++++---------------- 2 files changed, 439 insertions(+), 424 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 20a61ac57c..502198c645 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -18,7 +18,7 @@ module EDPhysiologyMod use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : hlm_use_fixed_biogeog use FatesInterfaceTypesMod, only : hlm_use_nocomp - use EDParamsMod , only : crop_lu_pft_vector + use EDParamsMod , only : crop_lu_pft_vector use FatesInterfaceTypesMod, only : hlm_nitrogen_spec use FatesInterfaceTypesMod, only : hlm_phosphorus_spec use FatesInterfaceTypesMod, only : hlm_use_tree_damage @@ -164,14 +164,14 @@ module EDPhysiologyMod public :: UpdateRecruitL2FR public :: UpdateRecruitStoicH public :: SetRecruitL2FR - + logical, parameter :: debug = .false. ! local debug flag character(len=*), parameter, private :: sourcefile = & __FILE__ integer :: istat ! return status code character(len=255) :: smsg ! Message string for deallocation errors - + integer, parameter :: dleafon_drycheck = 100 ! Drought deciduous leaves max days on check parameter real(r8), parameter :: decid_leaf_long_max = 1.0_r8 ! Maximum leaf lifespan for @@ -196,7 +196,7 @@ module EDPhysiologyMod ! computational problems. The current threshold ! is the same used in ED-2.2. - real(r8), parameter :: smp_lwr_bound = -1000000._r8 ! Imposed soil matric potential lower bound for + real(r8), parameter :: smp_lwr_bound = -1000000._r8 ! Imposed soil matric potential lower bound for ! frozen or excessively dry soils, used when ! computing water stress. ! ============================================================================ @@ -256,14 +256,14 @@ subroutine ZeroAllocationRates( currentSite ) end subroutine ZeroAllocationRates ! ============================================================================ - + subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) ! Arguments type(ed_site_type) :: csite type(fates_patch_type) :: cpatch type(bc_in_type), intent(in) :: bc_in - + ! Locals type(fates_cohort_type), pointer :: ccohort ! Current cohort @@ -283,10 +283,10 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) real(r8) :: repro_loss ! "" [kg] real(r8) :: sapw_loss ! "" [kg] real(r8) :: store_loss ! "" [kg] - real(r8) :: struct_loss ! "" [kg] + real(r8) :: struct_loss ! "" [kg] real(r8) :: dcmpy_frac ! fraction of mass going to each decomposition pool - real(r8) :: SF_val_CWD_frac_adj(4) !SF_val_CWD_frac adjusted based on cohort dbh - + real(r8) :: SF_val_CWD_frac_adj(4) !SF_val_CWD_frac adjusted based on cohort dbh + if(hlm_use_tree_damage .ne. itrue) return if(.not.damage_time) return @@ -298,12 +298,12 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) if(prt_params%woody(ccohort%pft)==ifalse ) cycle if(ccohort%isnew ) cycle - associate( ipft => ccohort%pft, & + associate( ipft => ccohort%pft, & agb_frac => prt_params%allom_agb_frac(ccohort%pft), & branch_frac => param_derived%branch_frac(ccohort%pft)) - + do_dclass: do cd = ccohort%crowndamage+1, nlevdamage - + call GetDamageFrac(ccohort%crowndamage, cd, ipft, cd_frac) ! now to get the number of damaged trees we multiply by damage frac @@ -315,7 +315,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) ! Create a new damaged cohort allocate(ndcohort) ! new cohort surviving but damaged if(hlm_use_planthydro.eq.itrue) call InitHydrCohort(csite,ndcohort) - + ! Initialize the PARTEH object and point to the ! correct boundary condition fields ndcohort%prt => null() @@ -323,37 +323,37 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) call InitPRTObject(ndcohort%prt) call ndcohort%InitPRTBoundaryConditions() call ndcohort%ZeroValues() - - ! nc_canopy_d is the new cohort that gets damaged + + ! nc_canopy_d is the new cohort that gets damaged call ccohort%Copy(ndcohort) - + ! new number densities - we just do damaged cohort here - ! undamaged at the end of the cohort loop once we know how many damaged to ! subtract - + ndcohort%n = num_trees_cd ndcohort%crowndamage = cd ! Remove these trees from the donor cohort ccohort%n = ccohort%n - num_trees_cd - - ! update crown area here - for cohort fusion and canopy organisation below + + ! update crown area here - for cohort fusion and canopy organisation below call carea_allom(ndcohort%dbh, ndcohort%n, csite%spread, & ipft, ndcohort%crowndamage, ndcohort%c_area) - + call GetCrownReduction(cd-ccohort%crowndamage, crown_loss_frac) do_element: do el = 1, num_elements - + litt => cpatch%litter(el) elflux_diags => csite%flux_diags%elem(el) - + ! Reduce the mass of the newly damaged cohort ! Fine-roots are not damaged as of yet ! only above-ground sapwood,structure and storage in ! branches is damaged/removed branch_loss_frac = crown_loss_frac * branch_frac * agb_frac - + leaf_loss = ndcohort%prt%GetState(leaf_organ,element_list(el))*crown_loss_frac repro_loss = ndcohort%prt%GetState(repro_organ,element_list(el))*crown_loss_frac sapw_loss = ndcohort%prt%GetState(sapw_organ,element_list(el))*branch_loss_frac @@ -364,7 +364,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) ! Transfer the biomass from the cohort's ! damage to the litter input fluxes ! ------------------------------------------------------ - + do dcmpy=1,ndcmpy dcmpy_frac = GetDecompyFrac(ipft,leaf_organ,dcmpy) litt%leaf_fines_in(dcmpy) = litt%leaf_fines_in(dcmpy) + & @@ -375,7 +375,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) elflux_diags%surf_fine_litter_input(ipft) = & elflux_diags%surf_fine_litter_input(ipft) + & (store_loss+leaf_loss+repro_loss) * ndcohort%n - + call adjust_SF_CWD_frac(ndcohort%dbh,ncwd,SF_val_CWD_frac,SF_val_CWD_frac_adj) do c = 1,ncwd @@ -383,12 +383,12 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) (sapw_loss + struct_loss) * & SF_val_CWD_frac_adj(c) * ndcohort%n / & cpatch%area - + elflux_diags%cwd_ag_input(c) = elflux_diags%cwd_ag_input(c) + & (struct_loss + sapw_loss) * & SF_val_CWD_frac_adj(c) * ndcohort%n end do - + end do do_element ! Applying the damage to the cohort, does not need to happen @@ -398,14 +398,14 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) call PRTDamageLosses(ndcohort%prt, sapw_organ, branch_loss_frac) call PRTDamageLosses(ndcohort%prt, store_organ, branch_loss_frac) call PRTDamageLosses(ndcohort%prt, struct_organ, branch_loss_frac) - - + + !----------- Insert new cohort into the linked list ! This list is going tall to short, lets add this new ! cohort into a taller position so we don't hit it again ! as the loop traverses ! --------------------------------------------------------------! - + ndcohort%shorter => ccohort if(associated(ccohort%taller))then ndcohort%taller => ccohort%taller @@ -415,7 +415,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) ndcohort%taller => null() endif ccohort%taller => ndcohort - + end if if_numtrees end do do_dclass @@ -423,7 +423,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) end associate ccohort => ccohort%shorter enddo - + return end subroutine GenerateDamageAndLitterFluxes @@ -472,29 +472,29 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) ! Calculate loss rate of viable seeds to litter call SeedDecay(litt, currentPatch, bc_in) - + ! Calculate seed germination rate, the status flags prevent ! germination from occuring when the site is in a drought ! (for drought deciduous) or too cold (for cold deciduous) call SeedGermination(litt, currentSite%cstatus, currentSite%dstatus(1:numpft), bc_in, currentPatch) - + ! Send fluxes from newly created litter into the litter pools ! This litter flux is from non-disturbance inducing mortality, as well ! as litter fluxes from live trees call CWDInput(currentSite, currentPatch, litt,bc_in) - + ! Only calculate fragmentation flux over layers that are active ! (RGK-Mar2019) SHOULD WE MAX THIS AT 1? DONT HAVE TO - + nlev_eff_decomp = max(bc_in%max_rooting_depth_index_col, 1) call CWDOut(litt,currentPatch%fragmentation_scaler,nlev_eff_decomp) - + ! Fragmentation flux to soil decomposition model [kg/site/day] site_mass%frag_out = site_mass%frag_out + currentPatch%area * & ( sum(litt%ag_cwd_frag) + sum(litt%bg_cwd_frag) + & sum(litt%leaf_fines_frag) + sum(litt%root_fines_frag) + & sum(litt%seed_decay) + sum(litt%seed_germ_decay)) - + ! Track total seed decay diagnostic in [kg/m2/day] diag%tot_seed_turnover = diag%tot_seed_turnover + & (sum(litt%seed_decay) + sum(litt%seed_germ_decay))*currentPatch%area*area_inv @@ -668,7 +668,7 @@ subroutine trim_canopy( currentSite ) real(r8) :: target_c_area real(r8) :: pft_leaf_lifespan ! Leaf lifespan of each PFT [years] - real(r8) :: leaf_long ! temporary leaf lifespan before accounting for deciduousness + real(r8) :: leaf_long ! temporary leaf lifespan before accounting for deciduousness !---------------------------------------------------------------------- currentPatch => currentSite%youngest_patch @@ -713,7 +713,7 @@ subroutine trim_canopy( currentSite ) call endrun(msg=errMsg(sourcefile, __LINE__)) endif - ! Find target leaf biomass. Here we assume that leaves would be fully flushed + ! Find target leaf biomass. Here we assume that leaves would be fully flushed ! (elongation factor = 1) call bleaf(currentcohort%dbh,ipft,& currentCohort%crowndamage, currentcohort%canopy_trim,1.0_r8, tar_bl) @@ -734,7 +734,7 @@ subroutine trim_canopy( currentSite ) else leaf_long = sum(prt_params%leaf_long_ustory(ipft,:)) end if - + ! PFT-level maximum SLA value, even if under a thick canopy (same units as slatop) sla_max = prt_params%slamax(ipft) @@ -749,7 +749,7 @@ subroutine trim_canopy( currentSite ) ! Calculate the cumulative total vegetation area index (no snow occlusion, stems and leaves) leaf_inc = dinc_vai(z) * & currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) - + ! Now calculate the cumulative top-down lai of the current layer's midpoint within the current cohort lai_layers_above = (dlower_vai(z) - dinc_vai(z)) * & currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) @@ -768,7 +768,7 @@ subroutine trim_canopy( currentSite ) kn = decay_coeff_vcmax(currentCohort%vcmax25top, & prt_params%leafn_vert_scaler_coeff1(ipft), & prt_params%leafn_vert_scaler_coeff2(ipft)) - + ! Nscaler value at leaf level z nscaler_levleaf = exp(-kn * cumulative_lai) ! Sla value at leaf level z after nitrogen profile scaling (m2/gC) @@ -954,8 +954,8 @@ subroutine phenology( currentSite, bc_in ) real(r8) :: elongf_prev ! Elongation factor from previous time real(r8) :: elongf_1st ! First guess for elongation factor integer :: ndays_pft_leaf_lifespan ! PFT life span of drought deciduous [days]. - ! This is the shortest between the PFT leaf - ! lifespan and the maximum lifespan of drought + ! This is the shortest between the PFT leaf + ! lifespan and the maximum lifespan of drought ! deciduous (see parameter decid_leaf_long_max ! at the beginning of this file). real(r8) :: phen_drought_threshold ! For drought hard-deciduous, this is the threshold @@ -966,14 +966,14 @@ subroutine phenology( currentSite, bc_in ) ! on the sign. If positive, these are soil ! volumetric water content [m3/m3]. If negative, ! the values are soil matric potential [mm]. Not - ! used for non-deciduous plants. Ignored for + ! used for non-deciduous plants. Ignored for ! non-deciduous plants. - real(r8) :: phen_moist_threshold ! For semi-deciduous, this is the threshold above + real(r8) :: phen_moist_threshold ! For semi-deciduous, this is the threshold above ! which flushing will be complete. This depends ! on the sign. If positive, these are soil ! volumetric water content [m3/m3]. If negative, ! the values are soil matric potential [mm]. - ! Ignored for hard-deciduous and evergreen + ! Ignored for hard-deciduous and evergreen ! plants. real(r8) :: phen_doff_time ! Minimum number of days that plants must remain ! leafless before flushing leaves again. @@ -1165,7 +1165,7 @@ subroutine phenology( currentSite, bc_in ) ! and thus %nchilldays will never go from zero to 1. The following logic ! when coupled with this fact will essentially prevent cold-deciduous ! plants from re-emerging in areas without at least some cold days - + if( (currentSite%cstatus == phen_cstat_notcold) .and. & (currentSite%cndaysleafoff > 400)) then ! remove leaves after a whole year, ! when there is no 'off' period. @@ -1182,7 +1182,7 @@ subroutine phenology( currentSite, bc_in ) - ! Loop through every PFT to assign the elongation factor. + ! Loop through every PFT to assign the elongation factor. ! Add PFT look to account for different PFT rooting depth profiles. pft_elong_loop: do ipft=1,numpft @@ -1204,9 +1204,9 @@ subroutine phenology( currentSite, bc_in ) nlevroot = max(2,min(ubound(currentSite%zi_soil,1),bc_in%max_rooting_depth_index_col)) ! The top most layer is typically very thin (~ 2cm) and dries rather quickly. Despite - ! being thin, it can have a non-negligible rooting fraction (e.g., using + ! being thin, it can have a non-negligible rooting fraction (e.g., using ! exponential_2p_root_profile with default parameters make the top layer to contain - ! about 7% of the total fine root density). To avoid overestimating dryness, we + ! about 7% of the total fine root density). To avoid overestimating dryness, we ! ignore the top layer when calculating the memory. rootfrac_notop = sum(currentSite%rootfrac_scr(2:nlevroot)) if ( rootfrac_notop <= nearzero ) then @@ -1225,13 +1225,13 @@ subroutine phenology( currentSite, bc_in ) currentSite%smp_memory (1,ipft) = 0._r8 do j = 2,nlevroot if(check_layer_water(bc_in%h2o_liqvol_sl(j),bc_in%tempk_sl(j)) ) then - currentSite%smp_memory (1,ipft) = currentSite%smp_memory (1,ipft) + & + currentSite%smp_memory (1,ipft) = currentSite%smp_memory (1,ipft) + & bc_in%smp_sl (j) * & currentSite%rootfrac_scr(j) / & rootfrac_notop else ! Nominal extreme suction for frozen or unreasonably dry soil - currentSite%smp_memory (1,ipft) = currentSite%smp_memory (1,ipft) + & + currentSite%smp_memory (1,ipft) = currentSite%smp_memory (1,ipft) + & smp_lwr_bound * & currentSite%rootfrac_scr(j) / & rootfrac_notop @@ -1278,7 +1278,7 @@ subroutine phenology( currentSite, bc_in ) ! for drought deciduous (local parameter). The sum term accounts for the ! total leaf life span of this cohort. ! Note we only use canopy leaf lifespan here and assume that understory cohorts - ! would behave the same as canopy cohorts with regards to phenology. + ! would behave the same as canopy cohorts with regards to phenology. ndays_pft_leaf_lifespan = & nint(ndays_per_year*min(decid_leaf_long_max,sum(prt_params%leaf_long(ipft,:)))) @@ -1301,10 +1301,10 @@ subroutine phenology( currentSite, bc_in ) case_drought_phen: select case (prt_params%stress_decid(ipft)) case (ihard_stress_decid) !---~--- - ! Default ("hard") drought deciduous phenology. The decision on whether to + ! Default ("hard") drought deciduous phenology. The decision on whether to ! abscise (shed) or flush leaves is in principle defined by the soil moisture - ! in the rooting zone. However, we must also account the time since last - ! abscission or flushing event, to avoid excessive "flickering" of the leaf + ! in the rooting zone. However, we must also account the time since last + ! abscission or flushing event, to avoid excessive "flickering" of the leaf ! elongation factor if soil moisture is right at the threshold. ! ! (MLO thought: maybe we should define moisture equivalents of GDD and chilling @@ -1342,7 +1342,7 @@ subroutine phenology( currentSite, bc_in ) !---~--- - ! Revision of the conditions, added an if/elseif/else structure to ensure only + ! Revision of the conditions, added an if/elseif/else structure to ensure only ! up to one change occurs at any given time. Also, prevent changes until the ! soil moisture memory is populated (the outer if check). !---~--- @@ -1387,7 +1387,7 @@ subroutine phenology( currentSite, bc_in ) elseif ( prolonged_on_period ) then ! LEAF OFF: DROUGHT DECIDUOUS LIFESPAN - ! Are the leaves rouhgly at the end of their lives? If so, shed leaves + ! Are the leaves rouhgly at the end of their lives? If so, shed leaves ! even if it is not dry. currentSite%dstatus(ipft) = phen_dstat_timeoff !alter status of site to 'leaves off' currentSite%dleafoffdate(ipft) = model_day_int !record leaf on date @@ -1588,7 +1588,7 @@ subroutine phenology_leafonoff(currentSite) real(r8) :: fnrt_drop_fraction ! Fine root relative drop fraction (0 = no drop, 1 = as much as leaves) real(r8) :: stem_drop_fraction ! Stem drop relative fraction (0 = no drop, 1 = as much as leaves) - real(r8) :: l2fr ! Leaf to fineroot biomass multiplier + real(r8) :: l2fr ! Leaf to fineroot biomass multiplier integer :: ipft ! Plant functional type index real(r8), parameter :: leaf_drop_fraction = 1.0_r8 @@ -1615,7 +1615,9 @@ subroutine phenology_leafonoff(currentSite) fnrt_drop_fraction = prt_params%phen_fnrt_drop_fraction(ipft) stem_drop_fraction = prt_params%phen_stem_drop_fraction(ipft) - l2fr = prt_params%allom_l2fr(ipft) + l2fr = currentCohort%l2fr ! Reading the L2FR from current cohort rather than parameter file + !l2fr = prt_params%allom_l2fr(ipft) + ! MLO. To avoid duplicating code for drought and cold deciduous PFTs, we first ! check whether or not it's time to flush or time to shed leaves, then @@ -1648,15 +1650,15 @@ subroutine phenology_leafonoff(currentSite) - ! Elongation factor for leaves is always the same as the site- and + ! Elongation factor for leaves is always the same as the site- and ! PFT-dependent factor computed in subroutine phenology. For evergreen - ! PFTs, this value should be always 1.0. + ! PFTs, this value should be always 1.0. currentCohort%efleaf_coh = currentSite%elong_factor(ipft) ! Find the effective "elongation factor" for fine roots and stems. The effective elongation - ! factor is a combination of the PFT leaf elongation factor (efleaf_coh) and the tissue drop + ! factor is a combination of the PFT leaf elongation factor (efleaf_coh) and the tissue drop ! fraction relative to leaves (xxxx_drop_fraction). When xxxx_drop_fraction is 0, the biomass - ! of tissue xxxx will not be impacted by phenology. If xxxx_drop_fraction is 1, the biomass + ! of tissue xxxx will not be impacted by phenology. If xxxx_drop_fraction is 1, the biomass ! of tissue xxxx will be as impacted by phenology as leaf biomass. Intermediate values will ! allow a more moderate impact of phenology in tissue xxxx relative to leaves. currentCohort%effnrt_coh = 1.0_r8 - (1.0_r8 - currentCohort%efleaf_coh ) * fnrt_drop_fraction @@ -1709,7 +1711,7 @@ subroutine phenology_leafonoff(currentSite) call PRTPhenologyFlush(currentCohort%prt, ipft, fnrt_organ, & store_c_transfer_frac*fnrt_deficit_c/total_deficit_c) - ! MLO - stem_drop_fraction is a PFT parameter, do we really need this + ! MLO - stem_drop_fraction is a PFT parameter, do we really need this ! check for woody/non-woody PFT? if ( prt_params%woody(ipft) == ifalse ) then call PRTPhenologyFlush(currentCohort%prt, ipft, sapw_organ, & @@ -1739,7 +1741,7 @@ subroutine phenology_leafonoff(currentSite) ! Find the effective fraction to drop. This fraction must be calculated every time ! because we must account for partial abscission. The simplest approach is to simply - ! use the ratio between the target and the original biomass of each pool. The + ! use the ratio between the target and the original biomass of each pool. The ! max(tissue_c,nearzero) is overly cautious, because leaf_c = 0 would imply that ! leaves are already off, and this wouldn't be considered shedding time. eff_leaf_drop_fraction = max( 0.0_r8, min( 1.0_r8,1.0_r8 - target_leaf_c / max( leaf_c , nearzero ) ) ) @@ -1945,13 +1947,13 @@ subroutine calculate_SP_properties(htop, tlai, tsai, parea, pft, crown_damage, ! calculate leaf carbon from target treelai canopylai(:) = 0._r8 leaf_c = leafc_from_treelai(tlai, tsai, pft, c_area, cohort_n, canopy_layer, vcmax25top) - + ! check that the inverse calculation of leafc from treelai is the same as the ! standard calculation of treelai from leafc. Maybe can delete eventually? call tree_lai_sai(leaf_c, pft, c_area, cohort_n, canopy_layer, canopylai, vcmax25top, & dbh, crown_damage, 1.0_r8, 1.0_r8, 11, check_treelai, dummy_treesai) - + if (abs(tlai - check_treelai) > area_error_2) then !this is not as precise as nearzero write(fates_log(),*) 'error in validate treelai', tlai, check_treelai, tlai - check_treelai write(fates_log(),*) 'tree_lai inputs: ', pft, c_area, cohort_n, canopy_layer, vcmax25top @@ -1991,7 +1993,7 @@ subroutine assign_cohort_SP_properties(currentCohort, htop, tlai, tsai, parea, i ! translates them into a FATES structure with one patch and one cohort per PFT. ! The leaf area of the cohort is modified each day to match that asserted by the HLM - + ! ARGUMENTS type(fates_cohort_type), intent(inout), target :: currentCohort ! cohort object real(r8), intent(in) :: tlai ! target leaf area index from SP inputs [m2/m2] @@ -2039,7 +2041,7 @@ subroutine assign_cohort_SP_properties(currentCohort, htop, tlai, tsai, parea, i end subroutine assign_cohort_SP_properties ! ===================================================================================== - + subroutine SeedUpdate( currentSite ) ! ----------------------------------------------------------------------------------- @@ -2116,7 +2118,7 @@ subroutine SeedUpdate( currentSite ) ! of seeds [kg] released by the plant, per the mass_fraction ! specified as input. This routine will also remove the mass ! from the parteh state-variable. - + call PRTReproRelease(currentCohort%prt,repro_organ,element_id, & 1.0_r8, seed_prod) @@ -2159,18 +2161,18 @@ subroutine SeedUpdate( currentSite ) ! If we are using the Tree Recruitment Scheme (TRS) with or w/o seedling dynamics if ( any(hlm_regeneration_model == [TRS_regeneration, TRS_no_seedling_dyn]) .and. & prt_params%allom_dbh_maxheight(pft) > min_max_dbh_for_trees) then - - ! Send a fraction of reproductive carbon to litter to account for + + ! Send a fraction of reproductive carbon to litter to account for ! non-seed reproductive carbon (e.g. flowers, fruit, etc.) - litt%seed_decay(pft) = litt%seed_in_local(pft) * (1.0_r8 - EDPftvarcon_inst%repro_frac_seed(pft)) - + litt%seed_decay(pft) = litt%seed_in_local(pft) * (1.0_r8 - EDPftvarcon_inst%repro_frac_seed(pft)) + ! Note: The default regeneration scheme sends all reproductive carbon to seed end if !Use TRS - + ! If there is forced external seed rain, we calculate the input mass flux ! from the different elements, using the mean stoichiometry of new ! recruits for the current patch and lowest canopy position - + select case(element_id) case(carbon12_element) seed_stoich = 1._r8 @@ -2183,15 +2185,15 @@ subroutine SeedUpdate( currentSite ) write(fates_log(), *) 'while defining forced external seed mass flux' call endrun(msg=errMsg(sourcefile, __LINE__)) end select - + ! Seed input from external sources (user param seed rain, or dispersal model) ! Include both prescribed seed_suppl and seed_in dispersed from neighbouring gridcells seed_in_external = seed_stoich*(currentSite%seed_in(pft)/area + EDPftvarcon_inst%seed_suppl(pft)*years_per_day) ![kg/m2/day] litt%seed_in_extern(pft) = litt%seed_in_extern(pft) + seed_in_external - + ! Seeds entering externally [kg/site/day] site_mass%seed_in = site_mass%seed_in + seed_in_external*currentPatch%area - end if !use this pft + end if !use this pft enddo currentPatch => currentPatch%younger @@ -2203,7 +2205,7 @@ subroutine SeedUpdate( currentSite ) site_mass%seed_out = site_mass%seed_out + site_seed_rain(pft)*site_disp_frac(pft) ![kg/site/day] currentSite%seed_out(pft) = currentSite%seed_out(pft) + site_seed_rain(pft)*site_disp_frac(pft) ![kg/site/day] end do - + end do el_loop return @@ -2217,27 +2219,27 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) ! 1. Flux from seed pool into leaf litter pool ! 2. If the TRS with seedling dynamics is on (hlm_regeneration_model = 3) ! then we calculate seedling mortality here (i.e. flux from seedling pool - ! (into leaf litter pool) + ! (into leaf litter pool) ! ! !ARGUMENTS type(litter_type) :: litt type(fates_patch_type), intent(in) :: currentPatch ! ahb added this - type(bc_in_type), intent(in) :: bc_in ! ahb added this + type(bc_in_type), intent(in) :: bc_in ! ahb added this ! ! !LOCAL VARIABLES: integer :: pft real(r8) :: seedling_layer_par ! cumulative sum of PAR at the seedling layer (MJ) - ! over prior window of days defined by + ! over prior window of days defined by ! fates_trs_seedling_mort_par_timescale real(r8) :: seedling_light_mort_rate ! daily seedling mortality rate from light stress real(r8) :: seedling_h2o_mort_rate ! daily seedling mortality rate from moisture stress real(r8) :: seedling_mdds ! moisture deficit days accumulated in the seedling layer - + !---------------------------------------------------------------------- - + ! 1. Seed mortality (i.e. flux from seed bank to litter) - + ! default value from Liscke and Loffler 2006 ; making this a PFT-specific parameter ! decays the seed pool according to exponential model ! seed_decay_rate is in yr-1 @@ -2245,9 +2247,9 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) ! Assume that decay rates are same for all chemical species !===================================================================================== - do pft = 1,numpft - - ! If the TRS is switched off or the pft can't get big enough to be considered a tree + do pft = 1,numpft + + ! If the TRS is switched off or the pft can't get big enough to be considered a tree ! then use FATES default regeneration. if ( hlm_regeneration_model == default_regeneration .or. & prt_params%allom_dbh_maxheight(pft) < min_max_dbh_for_trees ) then @@ -2259,45 +2261,45 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) end if ! If the TRS is switched on and the pft is a tree then add non-seed reproductive biomass - ! to the seed decay flux. This was added to litt%seed_decay in the previously called SeedIn + ! to the seed decay flux. This was added to litt%seed_decay in the previously called SeedIn ! subroutine if ( any(hlm_regeneration_model == [TRS_regeneration, TRS_no_seedling_dyn]) .and. & prt_params%allom_dbh_maxheight(pft) > min_max_dbh_for_trees ) then - + litt%seed_decay(pft) = litt%seed_decay(pft) + &! From non-seed reproductive biomass (added in ! in the SeedIn subroutine. litt%seed(pft) * EDPftvarcon_inst%seed_decay_rate(pft)*years_per_day - - end if + + end if ! If the TRS is switched on with seedling dynamics (hlm_regeneration_model = 2) ! then calculate seedling mortality. if_trs_germ_decay: if ( hlm_regeneration_model == TRS_regeneration .and. & prt_params%allom_dbh_maxheight(pft) > min_max_dbh_for_trees ) then - + !---------------------------------------------------------------------- ! Seedling mortality (flux from seedling pool to litter) ! Note: The TRS uses the litt%seed_germ data struture to track seedlings ! ! Step 1. Calculate the daily seedling mortality rate from light stress ! - ! Calculate the cumulative light at the seedling layer over a prior number of + ! Calculate the cumulative light at the seedling layer over a prior number of ! days determined by the "fates_tres_seedling_mort_par_timescale" parameter. - seedling_layer_par = currentPatch%sdlng_mort_par%GetMean() * megajoules_per_joule * & - sec_per_day * sdlng_mort_par_timescale - + seedling_layer_par = currentPatch%sdlng_mort_par%GetMean() * megajoules_per_joule * & + sec_per_day * sdlng_mort_par_timescale + ! Calculate daily seedling mortality rate from light seedling_light_mort_rate = exp( EDPftvarcon_inst%seedling_light_mort_a(pft) * & - seedling_layer_par + EDPftvarcon_inst%seedling_light_mort_b(pft) ) - + seedling_layer_par + EDPftvarcon_inst%seedling_light_mort_b(pft) ) + ! Step 2. Calculate the daily seedling mortality rate from moisture stress - + ! Get the current seedling moisture deficit days (tracked as a pft-specific exponential ! average) - seedling_mdds = currentPatch%sdlng_mdd(pft)%p%GetMean() - + seedling_mdds = currentPatch%sdlng_mdd(pft)%p%GetMean() + ! Calculate seedling mortality as a function of moisture deficit days (mdd) ! If the seedling mmd value is below a critical threshold then moisture-based mortality is zero if (seedling_mdds < EDPftvarcon_inst%seedling_mdd_crit(pft)) then @@ -2307,23 +2309,23 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) EDPftvarcon_inst%seedling_h2o_mort_b(pft) * seedling_mdds + & EDPftvarcon_inst%seedling_h2o_mort_c(pft) end if ! mdd threshold check - + ! Step 3. Sum modes of mortality (including background mortality) and send dead seedlings - ! to litter + ! to litter litt%seed_germ_decay(pft) = (litt%seed_germ(pft) * seedling_light_mort_rate) + & (litt%seed_germ(pft) * seedling_h2o_mort_rate) + & (litt%seed_germ(pft) * EDPftvarcon_inst%background_seedling_mort(pft) & * years_per_day) - + else - + litt%seed_germ_decay(pft) = litt%seed_germ(pft) * & EDPftvarcon_inst%seed_decay_rate(pft)*years_per_day end if if_trs_germ_decay - + enddo - + return end subroutine SeedDecay @@ -2331,7 +2333,7 @@ end subroutine SeedDecay subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) ! ! !DESCRIPTION: - ! Flux from seed bank into the seedling pool + ! Flux from seed bank into the seedling pool ! ! !USES: @@ -2345,7 +2347,7 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) ! ! !LOCAL VARIABLES: integer :: pft - real(r8), parameter :: max_germination = 1.0_r8 ! Cap on germination rates. + real(r8), parameter :: max_germination = 1.0_r8 ! Cap on germination rates. ! KgC/m2/yr Lishcke et al. 2009 !Light and moisture-sensitive seedling emergence variables (ahb) @@ -2373,31 +2375,31 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) ! is seed_decay_rate(p)/germination_rate(p) ! and thus the mortality rate (in units of individuals) is the product of ! that times the ratio of (hypothetical) seed mass to recruit biomass - + !============================================================================================== do pft = 1,numpft ! If the TRS's seedling dynamics is switched off, then we use FATES's default approach - ! to germination + ! to germination if_tfs_or_def: if ( hlm_regeneration_model == default_regeneration .or. & hlm_regeneration_model == TRS_no_seedling_dyn .or. & prt_params%allom_dbh_maxheight(pft) < min_max_dbh_for_trees ) then - litt%seed_germ_in(pft) = min(litt%seed(pft) * EDPftvarcon_inst%germination_rate(pft), & + litt%seed_germ_in(pft) = min(litt%seed(pft) * EDPftvarcon_inst%germination_rate(pft), & max_germination)*years_per_day ! If TRS seedling dynamics is switched on we calculate seedling emergence (i.e. germination) ! as a pft-specific function of understory light and soil moisture. else if ( hlm_regeneration_model == TRS_regeneration .and. & - prt_params%allom_dbh_maxheight(pft) > min_max_dbh_for_trees ) then + prt_params%allom_dbh_maxheight(pft) > min_max_dbh_for_trees ) then ! Step 1. Calculate how germination rate is modified by understory light - ! This applies to photoblastic germinators (e.g. many tropical pioneers) + ! This applies to photoblastic germinators (e.g. many tropical pioneers) ! Calculate mean PAR at the seedling layer (MJ m-2 day-1) over the prior 24 hours seedling_layer_par = currentPatch%seedling_layer_par24%GetMean() * sec_per_day * megajoules_per_joule - ! Calculate the photoblastic germination rate modifier (Eq. 3 Hanbury-Brown et al., 2022) + ! Calculate the photoblastic germination rate modifier (Eq. 3 Hanbury-Brown et al., 2022) photoblastic_germ_modifier = seedling_layer_par / & (seedling_layer_par + EDPftvarcon_inst%par_crit_germ(pft)) @@ -2407,11 +2409,11 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) ! Get running mean of soil matric potential (mm of H2O suction) at the seedling rooting depth ! This running mean based on pft-specific seedling rooting depth. - seedling_layer_smp = currentPatch%sdlng_emerg_smp(pft)%p%GetMean() + seedling_layer_smp = currentPatch%sdlng_emerg_smp(pft)%p%GetMean() ! Calculate a soil wetness index (1 / -soil matric pontential (MPa) ) used by the TRS - ! to calculate seedling mortality from moisture stress. - wetness_index = 1.0_r8 / (seedling_layer_smp * (-1.0_r8) * mpa_per_mm_suction) + ! to calculate seedling mortality from moisture stress. + wetness_index = 1.0_r8 / (seedling_layer_smp * (-1.0_r8) * mpa_per_mm_suction) ! Step 3. Calculate the seedling emergence rate based on soil moisture and germination ! rate modifier (Step 1). See Eq. 4 of Hanbury-Brown et al., 2022 @@ -2420,7 +2422,7 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) if ( seedling_layer_smp .GE. EDPftvarcon_inst%seedling_psi_emerg(pft) ) then seedling_emerg_rate = photoblastic_germ_modifier * EDPftvarcon_inst%a_emerg(pft) * & wetness_index**EDPftvarcon_inst%b_emerg(pft) - else + else seedling_emerg_rate = 0.0_r8 @@ -2430,7 +2432,7 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) litt%seed_germ_in(pft) = litt%seed(pft) * seedling_emerg_rate end if if_tfs_or_def - + !set the germination only under the growing season...c.xu if ((prt_params%season_decid(pft) == itrue ) .and. & @@ -2474,10 +2476,10 @@ subroutine recruitment(currentSite, currentPatch, bc_in) integer :: el ! loop counter for element integer :: element_id ! element index consistent with definitions in PRTGenericMod integer :: iage ! age loop counter for leaf age bins - integer :: crowndamage ! crown damage class of the cohort [1 = undamaged, >1 = damaged] + integer :: crowndamage ! crown damage class of the cohort [1 = undamaged, >1 = damaged] real(r8) :: height ! new cohort height [m] real(r8) :: dbh ! new cohort DBH [cm] - real(r8) :: cohort_n ! new cohort density + real(r8) :: cohort_n ! new cohort density real(r8) :: l2fr ! leaf to fineroot biomass ratio [0-1] real(r8) :: c_leaf ! target leaf biomass [kgC] real(r8) :: c_fnrt ! target fine root biomass [kgC] @@ -2495,14 +2497,14 @@ subroutine recruitment(currentSite, currentPatch, bc_in) real(r8) :: m_struct ! structural mass (element agnostic) [kg] real(r8) :: m_store ! storage mass (element agnostic) [kg] real(r8) :: m_repro ! reproductive mass (element agnostic) [kg] - real(r8) :: efleaf_coh - real(r8) :: effnrt_coh - real(r8) :: efstem_coh + real(r8) :: efleaf_coh + real(r8) :: effnrt_coh + real(r8) :: efstem_coh real(r8) :: mass_avail ! mass of each nutrient/carbon available in the seed_germination pool [kg] - real(r8) :: mass_demand ! total mass demanded by the plant to achieve the stoichiometric - ! targets of all the organs in the recruits. Used for both [kg per plant] and [kg per cohort] - real(r8) :: stem_drop_fraction ! - real(r8) :: fnrt_drop_fraction ! + real(r8) :: mass_demand ! total mass demanded by the plant to achieve the stoichiometric + ! targets of all the organs in the recruits. Used for both [kg per plant] and [kg per cohort] + real(r8) :: stem_drop_fraction ! + real(r8) :: fnrt_drop_fraction ! real(r8) :: sdlng2sap_par ! running mean of PAR at the seedling layer [MJ/m2/day] real(r8) :: seedling_layer_smp ! soil matric potential at seedling rooting depth [mm H2O suction] integer, parameter :: recruitstatus = 1 ! whether the newly created cohorts are recruited or initialized @@ -2542,7 +2544,7 @@ subroutine recruitment(currentSite, currentPatch, bc_in) l2fr = currentSite%rec_l2fr(ft, currentPatch%NCL_p) crowndamage = 1 ! new recruits are undamaged - ! calculate DBH from initial height + ! calculate DBH from initial height call h2d_allom(height, ft, dbh) ! default assumption is that leaves are on @@ -2559,7 +2561,7 @@ subroutine recruitment(currentSite, currentPatch, bc_in) effnrt_coh = 1.0_r8 - fnrt_drop_fraction efstem_coh = 1.0_r8 - stem_drop_fraction leaf_status = leaves_off - end if + end if ! Or.. if the plant is drought deciduous, make sure leaf status is consistent with the ! leaf elongation factor. @@ -2576,8 +2578,8 @@ subroutine recruitment(currentSite, currentPatch, bc_in) ! whenever the elongation factor is non-zero. If the elongation factor is zero, then leaves are in ! the "off" state. if (efleaf_coh > 0.0_r8) then - leaf_status = leaves_on - else + leaf_status = leaves_on + else leaf_status = leaves_off end if end select @@ -2649,11 +2651,11 @@ subroutine recruitment(currentSite, currentPatch, bc_in) sec_per_day*megajoules_per_joule mass_avail = currentPatch%area* & - currentPatch%litter(el)%seed_germ(ft)* & + currentPatch%litter(el)%seed_germ(ft)* & EDPftvarcon_inst%seedling_light_rec_a(ft)* & - sdlng2sap_par**EDPftvarcon_inst%seedling_light_rec_b(ft) + sdlng2sap_par**EDPftvarcon_inst%seedling_light_rec_b(ft) - ! If soil moisture is below pft-specific seedling moisture stress threshold the + ! If soil moisture is below pft-specific seedling moisture stress threshold the ! recruitment does not occur. ilayer_seedling_root = minloc(abs(bc_in%z_sisl(:) - & EDPftvarcon_inst%seedling_root_depth(ft)), dim=1) @@ -2662,7 +2664,7 @@ subroutine recruitment(currentSite, currentPatch, bc_in) if (seedling_layer_smp < EDPftvarcon_inst%seedling_psi_crit(ft)) then mass_avail = 0.0_r8 - end if + end if end if ! End use TRS with seedling dynamics @@ -2719,7 +2721,7 @@ subroutine recruitment(currentSite, currentPatch, bc_in) m_repro = 0._r8 end select - + select case(hlm_parteh_mode) case (prt_carbon_allom_hyp, prt_cnp_flex_allom_hyp) @@ -2762,7 +2764,7 @@ subroutine recruitment(currentSite, currentPatch, bc_in) currentPatch%litter(el)%seed_germ(ft) - cohort_n / currentPatch%area * & (m_struct + m_leaf + m_fnrt + m_sapw + m_store + m_repro) end if - + end do ! cycle through the initial conditions, and makes sure that they are all initialized @@ -2876,7 +2878,7 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) site_mass => currentSite%mass_balance(element_pos(element_id)) ! Transfer litter from turnover of living plants - + currentCohort => currentPatch%shortest do while(associated(currentCohort)) @@ -2887,9 +2889,9 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) store_m_turnover = currentCohort%prt%GetTurnover(store_organ,element_id) fnrt_m_turnover = currentCohort%prt%GetTurnover(fnrt_organ,element_id) repro_m_turnover = currentCohort%prt%GetTurnover(repro_organ,element_id) - - + + store_m = currentCohort%prt%GetState(store_organ,element_id) fnrt_m = currentCohort%prt%GetState(fnrt_organ,element_id) repro_m = currentCohort%prt%GetState(repro_organ,element_id) @@ -3297,16 +3299,16 @@ subroutine CWDOut( litt, fragmentation_scaler, nlev_eff_decomp ) enddo end subroutine CWDOut - + subroutine UpdateRecruitL2FR(csite) - + ! When CNP is active, the l2fr (target leaf to fine-root biomass multiplier) ! is dynamic. We therefore update what the l2fr for recruits ! are, taking an exponential moving average of all plants that ! are within recruit size limitations (less than recruit size + delta) ! and less than the max_count cohort. - + type(ed_site_type) :: csite type(fates_patch_type), pointer :: cpatch type(fates_cohort_type), pointer :: ccohort @@ -3320,11 +3322,11 @@ subroutine UpdateRecruitL2FR(csite) real(r8), parameter :: max_delta = 5.0_r8 ! dbh tolerance, cm, consituting a recruit real(r8), parameter :: smth_wgt = 1._r8/300.0_r8 integer, parameter :: max_count = 3 - + ! Difference in dbh (cm) to consider a plant was recruited fairly recently if(hlm_parteh_mode .ne. prt_cnp_flex_allom_hyp) return - + rec_n(1:numpft,1:nclmax) = 0._r8 rec_l2fr0(1:numpft,1:nclmax) = 0._r8 @@ -3332,7 +3334,7 @@ subroutine UpdateRecruitL2FR(csite) do while(associated(cpatch)) rec_count(1:numpft,1:nclmax) = 0 - + ccohort => cpatch%shortest cloop: do while(associated(ccohort)) @@ -3381,16 +3383,16 @@ subroutine UpdateRecruitStoich(csite) integer :: ft ! functional type index integer :: cl ! canopy layer index real(r8) :: rec_l2fr_pft ! Actual l2fr of a pft in it's patch - + ! Update the total plant stoichiometry of a new recruit, based on the updated ! L2FR values if(hlm_parteh_mode .ne. prt_cnp_flex_allom_hyp) return - + cpatch => csite%youngest_patch do while(associated(cpatch)) cl = cpatch%ncl_p - + do ft = 1,numpft rec_l2fr_pft = csite%rec_l2fr(ft,cl) cpatch%nitr_repro_stoich(ft) = & @@ -3406,15 +3408,15 @@ subroutine UpdateRecruitStoich(csite) ccohort%pc_repro = NewRecruitTotalStoichiometry(ccohort%pft,rec_l2fr_pft,phosphorus_element) ccohort => ccohort%taller end do cloop - + cpatch => cpatch%older end do - + return end subroutine UpdateRecruitStoich ! ====================================================================== - + subroutine SetRecruitL2FR(csite) @@ -3422,9 +3424,9 @@ subroutine SetRecruitL2FR(csite) type(fates_patch_type), pointer :: cpatch type(fates_cohort_type), pointer :: ccohort integer :: ft,cl - + if(hlm_parteh_mode .ne. prt_cnp_flex_allom_hyp) return - + cpatch => csite%youngest_patch do while(associated(cpatch)) ccohort => cpatch%shortest @@ -3441,7 +3443,7 @@ subroutine SetRecruitL2FR(csite) cpatch => cpatch%older end do - + return end subroutine SetRecruitL2FR diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index 320e4ea3e1..3f06ac08a6 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -28,7 +28,7 @@ module PRTAllometricCNPMod use PRTGenericMod , only : num_organ_types use PRTGenericMod , only : prt_cnp_flex_allom_hyp use PRTGenericMod , only : StorageNutrientTarget - + use FatesAllometryMod , only : bleaf use FatesAllometryMod , only : bsap_allom use FatesAllometryMod , only : bfineroot @@ -70,8 +70,11 @@ module PRTAllometricCNPMod use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model + use elm_varctl , only : nyears_ad_carbon_only, spinup_state + use elm_time_manager , only: get_curr_date, get_curr_time_string + + - implicit none private @@ -105,31 +108,31 @@ module PRTAllometricCNPMod ! Total number of state variables integer, parameter :: num_vars = 18 - - + + ! Global identifiers for the two stoichiometry values integer,public, parameter :: stoich_growth_min = 1 ! Flag for stoichiometry associated with ! minimum needed for growth - + ! This is deprecated until a reasonable hypothesis is in place (RGK) - integer,public, parameter :: stoich_max = 2 ! Flag for stoichiometry associated with + integer,public, parameter :: stoich_max = 2 ! Flag for stoichiometry associated with ! maximum for that organ - + ! This is the ordered list of organs used in this module ! ------------------------------------------------------------------------------------- integer, parameter :: num_organs = 6 - + ! Converting from local to global organ id - integer, parameter,dimension(num_organs) :: l2g_organ_list = & + integer, parameter,dimension(num_organs) :: l2g_organ_list = & [leaf_organ, fnrt_organ, sapw_organ, store_organ, repro_organ, struct_organ] - + ! These are local indices associated with organs and quantities ! that can be integrated (namely, growth respiration during stature growth ! and dbh) - + integer, parameter :: leaf_id = 1 integer, parameter :: fnrt_id = 2 integer, parameter :: sapw_id = 3 @@ -141,7 +144,7 @@ module PRTAllometricCNPMod integer, parameter :: num_intgr_vars = 7 - + ! ------------------------------------------------------------------------------------- ! Input/Output Boundary Indices (These are public, and therefore ! each boundary condition across all modules must @@ -186,7 +189,7 @@ module PRTAllometricCNPMod integer, public, parameter :: acnp_bc_out_id_nefflux = 2 ! Daily exudation of N [kg] integer, public, parameter :: acnp_bc_out_id_pefflux = 3 ! Daily exudation of P [kg] integer, public, parameter :: acnp_bc_out_id_limiter = 4 ! The minimum of the Nutrient ratio over c ratio - + integer, parameter :: num_bc_out = 4 ! Total number of @@ -199,7 +202,7 @@ module PRTAllometricCNPMod integer,private, parameter :: intgr_parm_effnrt = 6 integer,private, parameter :: intgr_parm_efstem = 7 integer,private, parameter :: num_intgr_parm = 7 - + ! ------------------------------------------------------------------------------------- ! Define the size of the coorindate vector. For this hypothesis, there is only ! one pool per each species x organ combination, except for leaves (WHICH HAVE AGE) @@ -228,15 +231,15 @@ module PRTAllometricCNPMod ! Flags to select using the equivalent carbon method of co-limitation, ! or to just grow with available carbon and let it fix itself on the ! next step - + integer, parameter :: grow_lim_conly = 1 ! Just use C to decide stature on this step integer, parameter :: grow_lim_estNP = 2 ! Estimate equivalent C from N and P integer, parameter :: grow_lim_type = grow_lim_estNP - - ! Following growth, if desired, you can prioritize that + + ! Following growth, if desired, you can prioritize that ! reproductive tissues get balanced CNP logical, parameter :: prioritize_repro_nutr_growth = .true. - + ! If this parameter is true, then the fine-root l2fr optimization ! scheme will remove biomass from roots without restriction if the ! l2fr is getting smaller. @@ -253,7 +256,7 @@ module PRTAllometricCNPMod procedure :: DailyPRT => DailyPRTAllometricCNP procedure :: FastPRT => FastPRTAllometricCNP procedure :: GetNutrientTarget => GetNutrientTargetCNP - + ! Extended functions specific to Allometric CNP procedure :: CNPPrioritizedReplacement procedure :: CNPStatureGrowth @@ -278,9 +281,9 @@ module PRTAllometricCNPMod character(len=*), parameter, private :: sourcefile = __FILE__ logical, parameter :: debug = .false. - + public :: InitPRTGlobalAllometricCNP - + contains @@ -306,7 +309,7 @@ subroutine InitPRTGlobalAllometricCNP() if (istat/=0) call endrun(msg='allocate stat/=0:'//trim(smsg)//errMsg(sourcefile, __LINE__)) allocate(prt_global_acnp%state_descriptor(num_vars), stat=istat, errmsg=smsg) if (istat/=0) call endrun(msg='allocate stat/=0:'//trim(smsg)//errMsg(sourcefile, __LINE__)) - + prt_global_acnp%hyp_name = 'Allometric Flexible C+N+P' prt_global_acnp%hyp_id = prt_cnp_flex_allom_hyp @@ -375,12 +378,12 @@ subroutine DailyPRTAllometricCNP(this,phase) ! and nutrient cycling are not yet compatable though ! hence, we simply return from any phase but phase 1 - + ! Pointers to in-out bcs real(r8),pointer :: dbh ! Diameter at breast height [cm] real(r8),pointer :: resp_excess ! Respiration of any un-allocatable C real(r8),pointer :: l2fr ! Leaf to fineroot ratio of target biomass - + ! Input only bcs integer :: ipft ! Plant Functional Type index real(r8) :: c_gain ! Daily carbon balance for this cohort [kgC] @@ -430,8 +433,8 @@ subroutine DailyPRTAllometricCNP(this,phase) ! damage module. Since this is incompatible with CNP ! Ignore all subsequent calls after the first if (phase.ne.1) return - - + + ! In/out boundary conditions resp_excess => this%bc_inout(acnp_bc_inout_id_resp_excess)%rval dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval @@ -447,7 +450,7 @@ subroutine DailyPRTAllometricCNP(this,phase) resp_excess = 0._r8 resp_excess0 = resp_excess - + ! integrator variables ! Copy the input only boundary conditions into readable local variables @@ -472,7 +475,7 @@ subroutine DailyPRTAllometricCNP(this,phase) if(p_uptake_mode.eq.prescribed_p_uptake) then p_gain = 1.e3_r8 end if - + n_gain0 = n_gain p_gain0 = p_gain c_gain0 = c_gain @@ -502,8 +505,8 @@ subroutine DailyPRTAllometricCNP(this,phase) ! and then attempt to get them up to stoichiometry targets. ! =================================================================================== - - + + ! Remember the original C,N,P states to help with final ! evaluation of how much was allocated ! ----------------------------------------------------------------------------------- @@ -517,8 +520,8 @@ subroutine DailyPRTAllometricCNP(this,phase) i_var = prt_global%sp_organ_map(i_org,phosphorus_element) state_p0(i_org) = this%variables(i_var)%val(1) end do - - + + ! Output only boundary conditions c_efflux => this%bc_out(acnp_bc_out_id_cefflux)%rval; c_efflux = 0._r8 n_efflux => this%bc_out(acnp_bc_out_id_nefflux)%rval; n_efflux = 0._r8 @@ -539,15 +542,15 @@ subroutine DailyPRTAllometricCNP(this,phase) p_gain = p_gain + sum(this%variables(store_p_id)%val(:)) this%variables(store_p_id)%val(:) = 0._r8 - - + + ! =================================================================================== ! Step 2. Prioritized allocation to replace tissues from turnover, and/or pay ! any un-paid maintenance respiration from storage. ! =================================================================================== - + call this%CNPPrioritizedReplacement(c_gain, n_gain, p_gain, target_c) - + sum_c = 0._r8 do i = 1,num_organs i_org = l2g_organ_list(i) @@ -563,10 +566,10 @@ subroutine DailyPRTAllometricCNP(this,phase) end do call endrun(msg=errMsg(sourcefile, __LINE__)) end if - + ! =================================================================================== ! Step 3. Grow out the stature of the plant by allocating to tissues beyond - ! current targets. + ! current targets. ! Attempts have been made to get all pools and species closest to allometric ! targets based on prioritized relative demand and allometry functions. ! =================================================================================== @@ -590,7 +593,7 @@ subroutine DailyPRTAllometricCNP(this,phase) end if ! =================================================================================== - ! Step 3. + ! Step 3. ! At this point, at least 1 of the 3 resources have been used up. ! Allocate the remaining resources, or as a last resort, efflux them. ! =================================================================================== @@ -616,7 +619,7 @@ subroutine DailyPRTAllometricCNP(this,phase) call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if - + if( abs(c_gain) > calloc_abs_error) then write(fates_log(),*) 'Allocation scheme should had used up all mass gain pools' write(fates_log(),*) 'Any mass that cannot be allocated should be effluxed' @@ -627,11 +630,11 @@ subroutine DailyPRTAllometricCNP(this,phase) ! Perform a final tally on what was used (allocated) ! Since this is also a check against what was available ! we include what is lost through respiration of excess storage - + allocated_c = (resp_excess-resp_excess0) + c_efflux allocated_n = n_efflux allocated_p = p_efflux - + ! Update the allocation flux diagnostic arrays for each 3 elements do i = 1,num_organs @@ -642,27 +645,27 @@ subroutine DailyPRTAllometricCNP(this,phase) this%variables(i_var)%net_alloc(1) + (this%variables(i_var)%val(1) - state_c0(i_org)) allocated_c = allocated_c + (this%variables(i_var)%val(1) - state_c0(i_org)) - + i_var = prt_global%sp_organ_map(i_org,nitrogen_element) this%variables(i_var)%net_alloc(1) = & this%variables(i_var)%net_alloc(1) + (this%variables(i_var)%val(1) - state_n0(i_org)) allocated_n = allocated_n + (this%variables(i_var)%val(1) - state_n0(i_org)) - + i_var = prt_global%sp_organ_map(i_org,phosphorus_element) this%variables(i_var)%net_alloc(1) = & this%variables(i_var)%net_alloc(1) + (this%variables(i_var)%val(1) - state_p0(i_org)) allocated_p = allocated_p + (this%variables(i_var)%val(1) - state_p0(i_org)) - + end do - + if(debug) then ! Error Check: Do a final balance between how much mass ! we had to work with, and how much was allocated - - if ( abs(allocated_c - (c_gain0-c_gain)) > calloc_abs_error .or. & + + if ( abs(allocated_c - (c_gain0-c_gain)) > calloc_abs_error .or. & abs(allocated_n - (n_gain0-n_gain)) > calloc_abs_error .or. & abs(allocated_p - (p_gain0-p_gain)) > calloc_abs_error ) then write(fates_log(),*) 'CNP allocation scheme did not balance mass.' @@ -683,7 +686,7 @@ subroutine DailyPRTAllometricCNP(this,phase) ! and pass that back as an output, otherwise ! we set the gains to what we started with so that ! it can be used again for mass balance checking and diagnostics - + if(n_uptake_mode.eq.prescribed_n_uptake) then n_gain = n_gain0-n_gain else @@ -699,32 +702,32 @@ subroutine DailyPRTAllometricCNP(this,phase) ! If fine-roots are allocated above their ! target (perhaps with some buffer, but perhaps not) - ! then + ! then call this%TrimFineRoot() return end subroutine DailyPRTAllometricCNP - + function SafeLog(val) result(logval) ! The log functions used to transform storage ratios ! need not be large. Even a ratio of 10 is sending a strong signal to the ! root adaptation algorithm to change course pretty strongly. We set ! bounds of e3 here to prevent numerical overflows and underflows - + real(r8) :: val real(r8) :: logval real(r8), parameter :: safelog_min = 0.001_r8 !Don't pass anything smaller to a log real(r8), parameter :: safelog_max = 1000._r8 logval = log(max(safelog_min,min(safelog_max,val))) - + end function SafeLog - + ! ===================================================================================== - + subroutine CNPAdjustFRootTargets(this, target_c, target_dcdd) class(cnp_allom_prt_vartypes) :: this @@ -744,7 +747,7 @@ subroutine CNPAdjustFRootTargets(this, target_c, target_dcdd) real(r8) :: cn_ratio, cp_ratio ! ratio of relative C storage over relative N or P storage real(r8) :: dcxdt_ratio ! log change (derivative) of the maximum of the N/C and P/C storage ratio real(r8) :: cx_logratio ! log Maximum of the C/N and C/P storage ratio - real(r8), pointer :: cx_int ! Integration of the cx_logratio + real(r8), pointer :: cx_int ! Integration of the cx_logratio real(r8), pointer :: cx0 ! The log of the cx ratio from previous time-step real(r8), pointer :: ema_dcxdt ! the EMA of the change in log storage ratio @@ -802,7 +805,7 @@ subroutine CNPAdjustFRootTargets(this, target_c, target_dcdd) ! Calculate the relative phosphorus storage fraction, ! over the relative carbon storage fraction. - store_nut_max = this%GetNutrientTarget(phosphorus_element,store_organ,stoich_growth_min) + store_nut_max = this%GetNutrientTarget(phosphorus_element,store_organ,stoich_growth_min) store_nut_act = max(0.001_r8*store_nut_max, & this%GetState(store_organ, phosphorus_element) + & @@ -871,19 +874,19 @@ end subroutine CNPAdjustFRootTargets ! ===================================================================================== subroutine TrimFineRoot(this) - + ! The following section allows forceful turnover of fine-roots if a new L2FR is generated ! that is lower than the previous l2fr. The maintenance turnover (background) rate ! will automatically accomodate a lower l2fr, but if the change is large it will ! not keep pace. Note 1: however, that the algorithm for calculating l2fr will prevent ! large drops in l2fr (unless that safegaurd is removed). Note 2: this section may also ! generate mass check errors in the main CNPAllocation routine, this is because the "val" is - ! changing but the net_allocated is not reciprocating, which is expected. - + ! changing but the net_allocated is not reciprocating, which is expected. + ! Keep a buffer above the L2FR in the hopes that natural turnover will catch ! up. class(cnp_allom_prt_vartypes) :: this - + real(r8) :: fnrt_flux_c real(r8) :: turn_flux_c real(r8) :: store_flux_c @@ -892,9 +895,9 @@ subroutine TrimFineRoot(this) real(r8) :: target_fnrt_c real(r8),parameter :: nday_buffer = 0._r8 real(r8),parameter :: fnrt_opt_eff = 0._r8 ! If we want to transfer resources to storage - + if(.not.use_unrestricted_contraction)return - + associate( ipft => this%bc_in(acnp_bc_in_id_pft)%ival, & l2fr => this%bc_inout(acnp_bc_inout_id_l2fr)%rval, & dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval, & @@ -936,17 +939,17 @@ subroutine TrimFineRoot(this) end associate return end subroutine TrimFineRoot - + ! ===================================================================================== - + subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) - - + + ! ----------------------------------------------------------------------------------- ! Alternative allocation hypothesis for the prioritized replacement phase. ! This is more similar to the current (04/2020) carbon only hypothesis. ! ----------------------------------------------------------------------------------- - + class(cnp_allom_prt_vartypes) :: this real(r8), intent(inout) :: c_gain real(r8), intent(inout) :: n_gain @@ -959,7 +962,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) real(r8), dimension(num_organs) :: deficit_c ! Deficit to get to target from current [kg] real(r8), dimension(num_organs) :: deficit_n ! Deficit to get to target from current [kg] real(r8), dimension(num_organs) :: deficit_p ! Deficit to get to target from current [kg] - + integer :: i, ii, i_org ! Loop indices (mostly for organs) integer :: i_var ! variable index integer :: i_pri ! loop index for priority @@ -984,8 +987,8 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ! the total number organs plus 1, which allows ! each organ to have its own level, and ignore ! the specialized priority 1 - - + + leaf_status = this%bc_in(acnp_bc_in_id_lstat)%ival elongf_leaf = this%bc_in(acnp_bc_in_id_efleaf)%rval elongf_fnrt = this%bc_in(acnp_bc_in_id_effnrt)%rval @@ -993,7 +996,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ipft = this%bc_in(acnp_bc_in_id_pft)%ival canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval - + n_max_priority = maxval(prt_params%organ_param_id(:)) if(n_max_priority>10 .or. n_max_priority<0)then write(fates_log(),*) 'was unable to interpret prt_params%organ_param_id' @@ -1019,7 +1022,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ! ! ----------------------------------------------------------------------------------- - + ! ----------------------------------------------------------------------------------- ! Preferential transfer of available carbon and nutrients into the highest ! priority pools, and maintenance respiration. We will loop through the available @@ -1030,7 +1033,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) curpri_org(:) = fates_unset_int ! reset "current-priority" organ ids i = 0 - do ii = 1, size(prt_params%organ_id,1) + do ii = 1, size(prt_params%organ_id,1) ! universal organ index from PRTGenericMod i_org = prt_params%organ_id(ii) @@ -1054,7 +1057,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) end do - + ! Number of pools in the current priority level n_curpri_org = i @@ -1075,12 +1078,12 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) end do sum_c_flux = max(0._r8,min(sum_c_demand,this%variables(store_c_id)%val(1)+c_gain)) - + if (sum_c_flux> nearzero ) then - + ! We pay this even if we don't have the carbon ! Just don't pay so much carbon that storage+carbon_balance can't pay for it - + do i = 1,n_curpri_org i_org = curpri_org(i) @@ -1088,10 +1091,10 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) c_flux = sum_c_flux*(prt_params%leaf_stor_priority(ipft) * & sum(this%variables(i_var)%turnover(:))/sum_c_demand) - + ! Add carbon to the pool this%variables(i_var)%val(1) = this%variables(i_var)%val(1) + c_flux - + ! Remove from daily carbon gain c_gain = c_gain - c_flux @@ -1100,36 +1103,36 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ! Determine nutrient demand and make tansfers do i = 1, n_curpri_org - + i_org = curpri_org(i) - + ! Update the nitrogen deficits ! Note that the nitrogen target is tied to the stoichiometry of the growing pool only (pos = 1) target_n = this%GetNutrientTarget(nitrogen_element,i_org,stoich_growth_min) deficit_n(i) = max(0.0_r8, target_n - this%GetState(i_org, nitrogen_element,1)) - + ! Update the phosphorus deficits (which are based off of carbon actual..) ! Note that the phsophorus target is tied to the stoichiometry of thegrowing pool only (also) target_p = this%GetNutrientTarget(phosphorus_element,i_org,stoich_growth_min) deficit_p(i) = max(0.0_r8, target_p - this%GetState(i_org, phosphorus_element,1)) end do - + ! Allocate nutrients at this priority level ! Nitrogen call ProportionalNutrAllocation(this,deficit_n(1:n_curpri_org), & n_gain, nitrogen_element, curpri_org(1:n_curpri_org)) - + ! Phosphorus call ProportionalNutrAllocation(this,deficit_p(1:n_curpri_org), & p_gain, phosphorus_element, curpri_org(1:n_curpri_org)) - + ! ----------------------------------------------------------------------------------- ! IV. if carbon balance is negative, re-coup the losses from storage ! if it is positive, give some love to storage carbon ! ----------------------------------------------------------------------------------- - + if( c_gain < 0.0_r8 ) then ! Storage will have to pay for any negative gains @@ -1137,26 +1140,26 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) c_gain = c_gain + store_c_flux this%variables(store_c_id)%val(1) = this%variables(store_c_id)%val(1) - store_c_flux - + else ! This is just a cap, don't fill up more than is needed (shouldn't even apply) store_below_target = max(target_c(store_organ) - this%variables(store_c_id)%val(1),0._r8) - + ! This is the desired need for carbon store_target_fraction = max(this%variables(store_c_id)%val(1)/target_c(store_organ),0._r8) store_demand = max(c_gain*(exp(-1.*store_target_fraction**4._r8) - exp( -1.0_r8 )),0._r8) ! The flux is the (positive) minimum of all three store_c_flux = min(store_below_target,store_demand) - + c_gain = c_gain - store_c_flux this%variables(store_c_id)%val(1) = this%variables(store_c_id)%val(1) + store_c_flux - + end if - - + + ! ----------------------------------------------------------------------------------- ! If carbon is still available, allocate to remaining high ! carbon balance is guaranteed to be >=0 beyond this point @@ -1166,9 +1169,9 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ! Bring all pools, in priority order, up to allometric targets if possible ! Repeat priority order 1 as well. ! ----------------------------------------------------------------------------------- - + priority_loop: do i_pri = 1, n_max_priority - + curpri_org(:) = fates_unset_int ! "current-priority" organ indices i = 0 @@ -1178,17 +1181,17 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) curpri_org(1) = store_organ i=1 end if - + ! Loop over all organs in the CNP routine, which - do ii = 1, size(prt_params%organ_id,1) + do ii = 1, size(prt_params%organ_id,1) ! universal organ index from PRTGenericMod i_org = prt_params%organ_id(ii) - + ! The priority code associated with this organ - + priority_code = int(prt_params%alloc_priority(ipft,ii)) - + ! Don't allow allocation to leaves if they are in an "off" status. ! (this prevents accidental re-flushing on the day they drop) if( any(leaf_status == [leaves_off,leaves_shedding]) .and. & @@ -1206,7 +1209,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) i_org = curpri_org(i) deficit_c(i) = max(0._r8,this%GetDeficit(carbon12_element,i_org,target_c(i_org))) end do - + ! Bring carbon up to target first, this order is required ! because we need to know the resulting carbon concentrations ! before we set the allometric targets for the nutrients @@ -1216,71 +1219,71 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) i_org = curpri_org(i) sum_c_demand = sum_c_demand + deficit_c(i) end do - + sum_c_flux = min(c_gain, sum_c_demand) - + ! Transfer carbon into pools if there is any if (sum_c_flux>nearzero) then do i = 1, n_curpri_org - + i_org = curpri_org(i) c_flux = sum_c_flux*deficit_c(i)/sum_c_demand - + ! Update the carbon pool i_var = prt_global%sp_organ_map(i_org,carbon12_element) this%variables(i_var)%val(1) = this%variables(i_var)%val(1) + c_flux - + ! Update carbon pools deficit deficit_c(i) = max(0._r8,deficit_c(i) - c_flux) - + ! Reduce the carbon gain c_gain = c_gain - c_flux - + end do end if ! Determine nutrient demand and make tansfers do i = 1, n_curpri_org - + i_org = curpri_org(i) ! Update the nitrogen deficits ! Note that the nitrogen target is tied to the stoichiometry of thegrowing pool only target_n = this%GetNutrientTarget(nitrogen_element,i_org,stoich_growth_min) deficit_n(i) = max(0.0_r8, target_n - this%GetState(i_org, nitrogen_element,1) ) - + ! Update the phosphorus deficits (which are based off of carbon actual..) ! Note that the phsophorus target is tied to the stoichiometry of thegrowing pool only (also) target_p = this%GetNutrientTarget(phosphorus_element,i_org,stoich_growth_min) deficit_p(i) = max(0.0_r8, target_p - this%GetState(i_org, phosphorus_element,1) ) end do - + ! Allocate nutrients at this priority level Nitrogen call ProportionalNutrAllocation(this,deficit_n(1:n_curpri_org), & n_gain, nitrogen_element, curpri_org(1:n_curpri_org)) - + ! Phosphorus call ProportionalNutrAllocation(this,deficit_p(1:n_curpri_org), & p_gain, phosphorus_element, curpri_org(1:n_curpri_org)) - + end do priority_loop - - + + return end subroutine CNPPrioritizedReplacement - + ! ===================================================================================== - + subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & target_c, target_dcdd) - - + + class(cnp_allom_prt_vartypes) :: this real(r8), intent(inout) :: c_gain ! Total daily C gain that remains to be used real(r8), intent(inout) :: n_gain ! Total N available for allocation @@ -1331,7 +1334,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & ! of organs (local ids) in the mask integer,dimension(num_organs) :: mask_gorgans ! List of organ global indices in the mask integer :: n_mask_organs - + ! Integrator error checking integer :: i_var integer :: nbins @@ -1349,11 +1352,11 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & real(r8) :: struct_c_target_tp1 real(r8) :: store_c_target_tp1 real(r8) :: sapw_area - + ! Integegrator variables ! These are not global because we want a unique instance for each time the routine is called ! ---------------------------------------------------------------------------------------- - + real(r8),dimension(num_intgr_vars) :: state_array ! Vector of carbon pools passed to integrator real(r8),dimension(num_intgr_vars) :: state_array_out ! Vector of carbon pools passed back from integrator logical,dimension(num_intgr_vars) :: state_mask ! Mask of active pools during integration @@ -1363,11 +1366,11 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & real(r8) :: intgr_params(num_intgr_parm) - + real(r8) :: neq_cgain, peq_cgain ! N and P equivalent c_gain spent on growth real(r8) :: cnp_gain ! used as a check to see efficiency of limited growth - - + + leaf_status = this%bc_in(acnp_bc_in_id_lstat)%ival elongf_leaf = this%bc_in(acnp_bc_in_id_efleaf)%rval @@ -1380,7 +1383,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval l2fr = this%bc_inout(acnp_bc_inout_id_l2fr)%rval ! This variable is not updated in this ! routine, and is therefore not a pointer - + if( c_gain <= calloc_abs_error ) then limiter = c_limited if((n_gain <= 0.1_r8*calloc_abs_error) .or. & @@ -1391,7 +1394,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & end if limiter = 0 - + ! If any of these resources is essentially tapped out, ! then there is no point in performing growth ! It also seems impossible that we would be in a leaf-off status @@ -1406,7 +1409,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & any(leaf_status == [leaves_off,leaves_shedding]) ) then return end if - + intgr_params(:) = fates_unset_r8 intgr_params(intgr_parm_ctrim) = this%bc_in(acnp_bc_in_id_ctrim)%rval intgr_params(intgr_parm_pft) = real(this%bc_in(acnp_bc_in_id_pft)%ival,r8) @@ -1418,7 +1421,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & state_mask(:) = .false. mask_organs(:) = fates_unset_int mask_gorgans(:) = fates_unset_int - + ! Go through and flag the integrating variables as either pools that ! are growing in this iteration, or not. At this point, if carbon for growth ! remains, it means that all pools are up to, or above the target. If @@ -1430,9 +1433,9 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & do i = 1, num_organs i_org = l2g_organ_list(i) - + cdeficit = this%GetDeficit(carbon12_element,i_org,target_c(i_org)) - + if ( cdeficit > calloc_abs_error ) then ! In this case, we somehow still have carbon to play with, ! yet one of the pools is below its current target @@ -1461,7 +1464,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & mask_organs(ii) = i mask_gorgans(ii) = i_org end if - + end if end do @@ -1480,7 +1483,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if - + ! fraction of carbon going towards reproduction. reproductive carbon is ! just different from the other pools. It is not based on proportionality, ! so its mask is set differently. We (inefficiently) just included @@ -1499,7 +1502,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & else repro_c_frac = prt_params%seed_alloc(ipft) + prt_params%seed_alloc_mature(ipft) end if - + ! If the TRS is switched on (with or w/o seedling dynamics) then reproductive allocation is ! a pft-specific function of dbh. This allows for the representation of different ! reproductive schedules (Wenk and Falster, 2015) @@ -1511,12 +1514,12 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & (1 + exp(prt_params%repro_alloc_b(ipft) + prt_params%repro_alloc_a(ipft)*dbh*mm_per_cm))) else - + write(fates_log(),*) 'unknown seed allocation and regeneration model, exiting' write(fates_log(),*) 'hlm_regeneration_model: ',hlm_regeneration_model call endrun(msg=errMsg(sourcefile, __LINE__)) - - end if ! regeneration switch + + end if ! regeneration switch if(repro_c_frac>nearzero)then @@ -1537,14 +1540,14 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & ! First objective is to find the extrapolated proportions of carbon going to ! each pool. This has nothing to do with carbon conservation, it is just used ! to make a rough prediction of how much nutrient is needed to match carbon - + total_dcostdd = 0._r8 do i = 1, n_mask_organs i_org = mask_gorgans(ii) total_dcostdd = total_dcostdd + target_dcdd(i_org) end do - + ! We can either proceed with stature growth by using all of the carbon ! available, or we can try to estimate the limitations of N and P ! and thereby reduce the amount of C we are willing to use to try @@ -1562,7 +1565,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & neq_cgain = n_gain/avg_nc peq_cgain = p_gain/avg_pc - + if(c_gain nearzero) then ! Initialize the adaptive integrator arrays and flags ! ----------------------------------------------------------------------------------- - + if(ODESolve == 2) then this%ode_opt_step = c_gstature end if - + ! If this flag is set to 0, then ! we have a successful integration ierr = 1 nsteps = 0 totalC = c_gstature - + ! Fill the state array with element masses for each organ do i = 1, num_organs i_org = l2g_organ_list(i) i_var = prt_global%sp_organ_map(i_org,carbon12_element) state_array(i) = this%variables(i_var)%val(1) end do - + state_mask(dbh_id) = .true. state_array(dbh_id) = dbh do_solve_check: do while( ierr .ne. 0 ) - + deltaC = min(totalC,this%ode_opt_step) if(ODESolve == 1) then - + call RKF45(AllomCNPGrowthDeriv,state_array,state_mask,deltaC,totalC, & max_trunc_error,intgr_params,state_array_out,this%ode_opt_step,step_pass) - + elseif(ODESolve == 2) then - + call Euler(AllomCNPGrowthDeriv,state_array,state_mask, & deltaC,totalC,intgr_params,state_array_out) ! Here we check to see if the solution is reasonably ! close to allometry, we also have to add up all leaf bins ! for this check. - + leafc_tp1 = state_array_out(leaf_id) i_var = prt_global%sp_organ_map(leaf_organ,carbon12_element) nbins = prt_global%state_descriptor(i_var)%num_pos @@ -1652,7 +1655,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & else this%ode_opt_step = 0.5_r8*deltaC end if - + else write(fates_log(),*) 'An integrator was chosen that DNE' write(fates_log(),*) 'ODESolve = ',ODESolve @@ -1660,24 +1663,24 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & end if nsteps = nsteps + 1 - + if(step_pass) then totalC = totalC - deltaC state_array(:) = state_array_out(:) end if - + ! TotalC should eventually be whittled down to near-zero ! -------------------------------------------------------------------------------- if_completed_solve: if( (totalC < calloc_abs_error) )then - + ierr = 0 - + ! Sum up the total flux predicted by the integrator, ! which SHOULD be c_gstature, except ! for integration errors. To make carbon ! perfectly preserved, we calculate this bias ! and make a linear (proportional) correction to all pools. - + sum_c_flux = 0.0_r8 do ii = 1, n_mask_organs i = mask_organs(ii) @@ -1685,35 +1688,35 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & i_var = prt_global%sp_organ_map(i_org,carbon12_element) sum_c_flux = sum_c_flux + (state_array(i) - this%variables(i_var)%val(1)) end do - + ! This is a correction factor that forces ! mass conservation c_flux_adj = c_gstature/sum_c_flux - + do ii = 1, n_mask_organs - + i = mask_organs(ii) i_org = mask_gorgans(ii) i_var = prt_global%sp_organ_map(i_org,carbon12_element) - + ! Calculate adjusted flux c_flux = (state_array(i) - this%variables(i_var)%val(1))*c_flux_adj - + ! update the carbon pool (in all pools flux goes into the first pool) this%variables(i_var)%val(1) = this%variables(i_var)%val(1) + c_flux - + ! Remove carbon from the daily gain c_gain = c_gain - c_flux - + end do - + ! Update dbh - dbh = state_array(dbh_id) - + dbh = state_array(dbh_id) + else if_step_exceedance: if (nsteps > max_substeps ) then - + write(fates_log(),*) 'CNP Plant Growth Integrator could not find' write(fates_log(),*) 'a solution in less than ',max_substeps,' tries' write(fates_log(),*) 'Aborting' @@ -1739,7 +1742,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & sapwc_tp1 = state_array_out(sapw_id) storec_tp1 = state_array_out(store_id) structc_tp1 = state_array_out(struct_id) - + call bleaf(dbh_tp1,ipft,crown_damage,canopy_trim, elongf_leaf, leaf_c_target_tp1) call bfineroot(dbh_tp1,ipft,canopy_trim,l2fr, elongf_fnrt, fnrt_c_target_tp1) call bsap_allom(dbh_tp1,ipft,crown_damage,canopy_trim, elongf_stem, sapw_area,sapw_c_target_tp1) @@ -1756,9 +1759,9 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & call endrun(msg=errMsg(sourcefile, __LINE__)) end if if_step_exceedance - + end if if_completed_solve - + end do do_solve_check ! Prioritize nutrient transfer to the reproductive pool @@ -1773,19 +1776,19 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & target_n = this%GetNutrientTarget(nitrogen_element,repro_organ,stoich_growth_min) deficit_n(1) = this%GetDeficit(nitrogen_element,repro_organ,target_n) n_flux = max(0._r8,min(n_gain,deficit_n(1))) - + target_p = this%GetNutrientTarget(phosphorus_element,repro_organ,stoich_growth_min) deficit_p(1) = this%GetDeficit(phosphorus_element,repro_organ,target_p) p_flux = max(0._r8,min(p_gain,deficit_p(1))) - + this%variables(repro_n_id)%val(1) = this%variables(repro_n_id)%val(1) + n_flux this%variables(repro_p_id)%val(1) = this%variables(repro_p_id)%val(1) + p_flux n_gain = n_gain - n_flux p_gain = p_gain - p_flux - + end if - + ! ----------------------------------------------------------------------------------- ! Nutrient Fluxes proportionally to each pool (these should be fully actualized) ! (this also removes from the gain pools) @@ -1797,24 +1800,24 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & i = mask_organs(ii) i_org = mask_gorgans(ii) - + target_n = this%GetNutrientTarget(nitrogen_element,i_org,stoich_growth_min) target_p = this%GetNutrientTarget(phosphorus_element,i_org,stoich_growth_min) deficit_n(ii) = this%GetDeficit(nitrogen_element,i_org,target_n) sum_n_demand = sum_n_demand+max(0._r8,deficit_n(ii)) - + deficit_p(ii) = this%GetDeficit(phosphorus_element,i_org,target_p) sum_p_demand = sum_p_demand+max(0._r8,deficit_p(ii)) - + end do ! TODO: mask_organs should be a vector of global organs - + ! Nitrogen - call ProportionalNutrAllocation(this,deficit_n(1:n_mask_organs), & + call ProportionalNutrAllocation(this,deficit_n(1:n_mask_organs), & n_gain, nitrogen_element,mask_gorgans(1:n_mask_organs)) - + ! Phosphorus call ProportionalNutrAllocation(this,deficit_p(1:n_mask_organs), & p_gain, phosphorus_element,mask_gorgans(1:n_mask_organs)) @@ -1823,7 +1826,7 @@ subroutine CNPStatureGrowth(this,c_gain, n_gain, p_gain, & return end subroutine CNPStatureGrowth - + ! ===================================================================================== subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & @@ -1833,13 +1836,13 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & class(cnp_allom_prt_vartypes) :: this real(r8), intent(inout) :: c_gain real(r8), intent(inout) :: n_gain - real(r8), intent(inout) :: p_gain + real(r8), intent(inout) :: p_gain real(r8), intent(inout) :: c_efflux real(r8), intent(inout) :: n_efflux real(r8), intent(inout) :: p_efflux real(r8) :: target_c(:) real(r8) :: target_dcdd(:) - + integer :: i real(r8), dimension(num_organs) :: deficit_n real(r8), dimension(num_organs) :: deficit_p @@ -1853,21 +1856,24 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & integer, pointer :: limiter real(r8) :: canopy_trim integer :: crown_damage - + + character(len=256) :: dateTimeString + integer :: yr, mon, day, sec + dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval ipft = this%bc_in(acnp_bc_in_id_pft)%ival resp_excess => this%bc_inout(acnp_bc_inout_id_resp_excess)%rval limiter => this%bc_out(acnp_bc_out_id_limiter)%ival crown_damage = this%bc_in(acnp_bc_in_id_cdamage)%ival - + ! ----------------------------------------------------------------------------------- ! If nutrients are still available, then we can bump up the values in the pools ! towards the OPTIMAL target values. ! ----------------------------------------------------------------------------------- do i = 1, num_organs - + ! Update the nitrogen and phosphorus deficits target_n = this%GetNutrientTarget(nitrogen_element,l2g_organ_list(i),stoich_growth_min) target_p = this%GetNutrientTarget(phosphorus_element,l2g_organ_list(i),stoich_growth_min) @@ -1879,18 +1885,18 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & deficit_n(i) = max(0._r8,this%GetDeficit(nitrogen_element,l2g_organ_list(i),target_n)) deficit_p(i) = max(0._r8,this%GetDeficit(phosphorus_element,l2g_organ_list(i),target_p)) - + end do - + ! ----------------------------------------------------------------------------------- ! Nutrient Fluxes proportionally to each pool (these should be fully actualized) ! (this also removes from the gain pools) ! ----------------------------------------------------------------------------------- - + ! Nitrogen - call ProportionalNutrAllocation(this,deficit_n(1:num_organs), & + call ProportionalNutrAllocation(this,deficit_n(1:num_organs), & n_gain, nitrogen_element, l2g_organ_list(1:num_organs)) - + ! Phosphorus call ProportionalNutrAllocation(this,deficit_p(1:num_organs), & p_gain, phosphorus_element, l2g_organ_list(1:num_organs)) @@ -1898,8 +1904,15 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! This routine updates the l2fr (leaf 2 fine-root multiplier) variable ! It will also update the target - call this%CNPAdjustFRootTargets(target_c,target_dcdd) - + + ! turn on the dynamic L2FR post supplemental N period + call get_curr_date(yr, mon, day, sec) + if (spinup_state == 1 .and. yr .gt. nyears_ad_carbon_only) then + call this%CNPAdjustFRootTargets(target_c,target_dcdd) + else if (spinup_state /= 1) then + call this%CNPAdjustFRootTargets(target_c,target_dcdd) + end if + ! ----------------------------------------------------------------------------------- ! If carbon is still available, lets cram some into storage overflow ! We will do this last, because we wanted the non-overflow storage @@ -1909,12 +1922,12 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & if(c_gain>calloc_abs_error) then if(store_c_overflow == retain_c_store_overflow)then - + total_c_flux = c_gain ! Transfer excess carbon into storage overflow this%variables(store_c_id)%val(1) = this%variables(store_c_id)%val(1) + total_c_flux c_gain = c_gain - total_c_flux - + elseif(store_c_overflow == burn_c_store_overflow) then ! Update carbon based allometric targets @@ -1922,29 +1935,29 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! Allow some overflow store_c_target = store_c_target * (1._r8 + prt_params%store_ovrflw_frac(ipft)) - + total_c_flux = max(0._r8,min(c_gain, store_c_target - this%variables(store_c_id)%val(1) )) - + ! Transfer excess carbon INTO storage overflow this%variables(store_c_id)%val(1) = this%variables(store_c_id)%val(1) + total_c_flux c_gain = c_gain - total_c_flux resp_excess = resp_excess + c_gain c_gain = 0._r8 - + elseif(store_c_overflow == exude_c_store_overflow)then - + ! Update carbon based allometric targets call bstore_allom(dbh,ipft,crown_damage,canopy_trim, store_c_target) - + ! Estimate the overflow store_c_target = store_c_target * (1._r8 + prt_params%store_ovrflw_frac(ipft)) - + total_c_flux = max(0.0, min(c_gain, store_c_target - this%variables(store_c_id)%val(1))) ! Transfer excess carbon into storage overflow this%variables(store_c_id)%val(1) = this%variables(store_c_id)%val(1) + total_c_flux c_gain = c_gain - total_c_flux - + end if end if @@ -1964,8 +1977,8 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & this%variables(store_p_id)%val(1) = this%variables(store_p_id)%val(1) + p_gain p_gain = 0 end if - - + + ! Figure out what to do with excess carbon and nutrients ! 1) excude through roots cap at 0 to flush out imprecisions @@ -1975,14 +1988,14 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! don't efflux anything, we will use the remainder ! n_gain and p_gain to specify the demand as what was used ! and what was uptaken - + if(n_uptake_mode.eq.prescribed_n_uptake) then n_efflux = 0._r8 else n_efflux = n_gain n_gain = 0._r8 end if - + if(p_uptake_mode.eq.prescribed_p_uptake) then p_efflux = 0._r8 else @@ -1992,8 +2005,8 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & c_efflux = c_gain c_gain = 0.0_r8 - - + + nullify(dbh) @@ -2019,7 +2032,7 @@ end subroutine FastPRTAllometricCNP ! ===================================================================================== - + function GetDeficit(this,element_id,organ_id,target_m) result(deficit_m) class(cnp_allom_prt_vartypes) :: this @@ -2029,7 +2042,7 @@ function GetDeficit(this,element_id,organ_id,target_m) result(deficit_m) integer :: i_var real(r8) :: deficit_m - + i_var = prt_global%sp_organ_map(organ_id,element_id) if(element_id.eq.carbon12_element) then @@ -2037,20 +2050,20 @@ function GetDeficit(this,element_id,organ_id,target_m) result(deficit_m) else deficit_m = target_m - this%variables(i_var)%val(1) end if - + return end function GetDeficit - + ! ===================================================================================== function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(target_m) - + class(cnp_allom_prt_vartypes) :: this integer, intent(in) :: element_id integer, intent(in) :: organ_id integer, intent(in),optional :: stoich_mode real(r8) :: target_m ! Target amount of nutrient for this organ [kg] - + real(r8) :: target_c real(r8),pointer :: dbh real(r8) :: canopy_trim @@ -2079,12 +2092,12 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe nc_repro = this%bc_in(acnp_bc_in_id_nc_repro)%rval pc_repro = this%bc_in(acnp_bc_in_id_pc_repro)%rval crown_damage = this%bc_in(acnp_bc_in_id_cdamage)%ival - + ! Storage of nutrients are assumed to have different compartments than ! for carbon, and thus their targets are not associated with a tissue ! but is more represented as a fraction of the maximum amount of nutrient ! that can be bound in non-reproductive tissues - + if(organ_id == store_organ) then call bleaf(dbh,ipft,crown_damage,canopy_trim, elongf_leaf, leaf_c_target) @@ -2098,18 +2111,18 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe ! non-reproductive organs if( element_id == nitrogen_element) then - + target_m = StorageNutrientTarget(ipft, element_id, & leaf_c_target*prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(leaf_organ)), & fnrt_c_target*prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(fnrt_organ)), & - sapw_c_target*prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(sapw_organ)), & + sapw_c_target*prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(sapw_organ)), & struct_c_target*prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(struct_organ))) else - + target_m = StorageNutrientTarget(ipft, element_id, & leaf_c_target*prt_params%phos_stoich_p1(ipft,prt_params%organ_param_id(leaf_organ)), & fnrt_c_target*prt_params%phos_stoich_p1(ipft,prt_params%organ_param_id(fnrt_organ)), & - sapw_c_target*prt_params%phos_stoich_p1(ipft,prt_params%organ_param_id(sapw_organ)), & + sapw_c_target*prt_params%phos_stoich_p1(ipft,prt_params%organ_param_id(sapw_organ)), & struct_c_target*prt_params%phos_stoich_p1(ipft,prt_params%organ_param_id(struct_organ))) end if @@ -2119,7 +2132,7 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe if( stoich_mode == stoich_max ) then target_m = target_m*(1._r8 + prt_params%store_ovrflw_frac(ipft)) end if - + elseif(organ_id == repro_organ) then target_c = this%variables(i_cvar)%val(1) @@ -2128,7 +2141,7 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe else target_m = target_c * pc_repro end if - + else @@ -2137,12 +2150,12 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe write(fates_log(),*) 'for non-reproductive and non-storage organs' call endrun(msg=errMsg(sourcefile, __LINE__)) end if - + ! In all cases, we want the first index because for non-leaves ! that is the only index, and for leaves, that is the newly ! growing index. target_c = this%variables(i_cvar)%val(1) - + if( stoich_mode == stoich_growth_min ) then if( element_id == nitrogen_element) then target_m = target_c * prt_params%nitr_stoich_p1(ipft,prt_params%organ_param_id(organ_id)) @@ -2173,9 +2186,9 @@ function GetNutrientTargetCNP(this,element_id,organ_id,stoich_mode) result(targe end function GetNutrientTargetCNP - + ! ===================================================================================== - + subroutine ProportionalNutrAllocation(this,deficit_m, gain_m, element_id, list) ! ----------------------------------------------------------------------------------- @@ -2202,32 +2215,32 @@ subroutine ProportionalNutrAllocation(this,deficit_m, gain_m, element_id, list) real(r8) :: sum_flux num_organs = size(list,dim=1) - + sum_deficit = 0._r8 do i = 1, num_organs i_org = list(i) sum_deficit = sum_deficit + max(0._r8,deficit_m(i)) end do - + if (sum_deficit>nearzero) then - + sum_flux = min(gain_m, sum_deficit) - + do i = 1, num_organs i_org = list(i) - + flux = sum_flux * max(0._r8,deficit_m(i))/sum_deficit i_var = prt_global%sp_organ_map(i_org,element_id) this%variables(i_var)%val(1) = this%variables(i_var)%val(1) + flux - + deficit_m(i) = deficit_m(i) - flux gain_m = gain_m - flux - + end do - + end if - + if(debug) then if(gain_m < -calloc_abs_error) then write(fates_log(),*) 'Somehow we have negative nutrient gain' @@ -2236,7 +2249,7 @@ subroutine ProportionalNutrAllocation(this,deficit_m, gain_m, element_id, list) call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if - + return end subroutine ProportionalNutrAllocation @@ -2274,7 +2287,7 @@ function AllomCNPGrowthDeriv(l_state_array,l_state_mask,cbalance,intgr_params) r integer :: ipft ! PFT index real(r8) :: canopy_trim ! Canopy trimming function (boundary condition [0-1] integer :: crown_damage ! Damage class - real(r8) :: l2fr ! leaf to fineroot biomass multiplier + real(r8) :: l2fr ! leaf to fineroot biomass multiplier real(r8) :: leaf_c_target ! target leaf biomass, dummy var (kgC) real(r8) :: fnrt_c_target ! target fine-root biomass, dummy var (kgC) real(r8) :: sapw_c_target ! target sapwood biomass, dummy var (kgC) @@ -2333,14 +2346,14 @@ function AllomCNPGrowthDeriv(l_state_array,l_state_mask,cbalance,intgr_params) r ! If the TRS is switched off then we use FATES's default reproductive allocation. if ( hlm_regeneration_model == default_regeneration .or. & - prt_params%allom_dbh_maxheight(ipft) < min_max_dbh_for_trees ) then ! The Tree Recruitment Scheme + prt_params%allom_dbh_maxheight(ipft) < min_max_dbh_for_trees ) then ! The Tree Recruitment Scheme ! is only for trees if (dbh <= prt_params%dbh_repro_threshold(ipft)) then repro_fraction = prt_params%seed_alloc(ipft) else repro_fraction = prt_params%seed_alloc(ipft) + prt_params%seed_alloc_mature(ipft) end if - + ! If the TRS is switched on (with or w/o seedling dynamics) then reproductive allocation is ! a pft-specific function of dbh. This allows for the representation of different ! reproductive schedules (Wenk and Falster, 2015) @@ -2354,8 +2367,8 @@ function AllomCNPGrowthDeriv(l_state_array,l_state_mask,cbalance,intgr_params) r write(fates_log(),*) 'unknown seed allocation and regeneration model, exiting' write(fates_log(),*) 'hlm_regeneration_model: ',hlm_regeneration_model call endrun(msg=errMsg(sourcefile, __LINE__)) - end if ! regeneration switch - + end if ! regeneration switch + else ! mask repro repro_fraction = 0._r8 end if !mask repro @@ -2410,9 +2423,9 @@ function AllomCNPGrowthDeriv(l_state_array,l_state_mask,cbalance,intgr_params) r write(fates_log(),*) 'repro fraction: ',repro_fraction call endrun(msg=errMsg(sourcefile, __LINE__)) end if - + dCdx(dbh_id) = (1.0_r8/total_dcostdd)*(1.0_r8 - repro_fraction) - + else if(repro_fraction this%bc_inout(acnp_bc_inout_id_dbh)%rval, & + + associate(dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval, & ipft => this%bc_in(acnp_bc_in_id_pft)%ival, & nc_repro => this%bc_in(acnp_bc_in_id_nc_repro)%rval, & pc_repro => this%bc_in(acnp_bc_in_id_pc_repro)%rval) - + if(state_mask(repro_id)) then - + ! If the TRS is switched off then we use FATES's default reproductive allocation. if ( hlm_regeneration_model == default_regeneration .or. & - prt_params%allom_dbh_maxheight(ipft) < min_max_dbh_for_trees ) then ! The Tree Recruitment Scheme + prt_params%allom_dbh_maxheight(ipft) < min_max_dbh_for_trees ) then ! The Tree Recruitment Scheme ! is only for trees if (dbh <= prt_params%dbh_repro_threshold(ipft)) then repro_c_frac = prt_params%seed_alloc(ipft) else repro_c_frac = prt_params%seed_alloc(ipft) + prt_params%seed_alloc_mature(ipft) end if - + ! If the TRS is switched on (with or w/o seedling dynamics) then reproductive allocation is ! a pft-specific function of dbh. This allows for the representation of different ! reproductive schedules (Wenk and Falster, 2015) @@ -2490,17 +2503,17 @@ subroutine EstimateGrowthNC(this,target_c,target_dcdd,state_mask,avg_nc,avg_pc) write(fates_log(),*) 'unknown seed allocation and regeneration model, exiting' write(fates_log(),*) 'hlm_regeneration_model: ',hlm_regeneration_model call endrun(msg=errMsg(sourcefile, __LINE__)) - end if ! regeneration switch + end if ! regeneration switch else ! state mask repro_c_frac = 0._r8 end if ! state mask - + ! Estimate the total weight total_w = 0._r8 avg_nc = 0._r8 avg_pc = 0._r8 - + if(state_mask(leaf_id)) then leaf_w = target_dcdd(leaf_organ) * (1._r8 - repro_c_frac) total_w = total_w + leaf_w @@ -2542,11 +2555,11 @@ subroutine EstimateGrowthNC(this,target_c,target_dcdd,state_mask,avg_nc,avg_pc) ! repro_w = total_w * repro_c_frac/(1-repro_c_frac) if(1._r8 - repro_c_frac < nearzero) then - repro_w = repro_c_frac + repro_w = repro_c_frac else repro_w = total_w * repro_c_frac/(1._r8 - repro_c_frac) end if - + total_w = total_w + repro_w avg_nc = avg_nc + repro_w * nc_repro avg_pc = avg_pc + repro_w * pc_repro @@ -2556,7 +2569,7 @@ subroutine EstimateGrowthNC(this,target_c,target_dcdd,state_mask,avg_nc,avg_pc) avg_pc = avg_pc / total_w end associate - + return end subroutine EstimateGrowthNC From c47a1721bfd025a2e6401e98440153641e981dd3 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 13 Mar 2025 13:27:23 -0700 Subject: [PATCH 023/194] update prescribed fire burnt area unit in param file --- parameter_files/fates_params_default.cdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 6c7dd58a7c..cac0364390 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -922,7 +922,7 @@ variables: fates_rxfire_wind_lwthreshold:units = "m/s"; fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold for conducting prescribeb fire"; double fates_rxfire_AB ; - fates_rxfire_AB:units = "m2/day"; + fates_rxfire_AB:units = "fraction/day"; fates_rxfire_AB:long_name= "daily burn capacity of prescribed fire"; double fates_rxfire_min_threshold ; fates_rxfire_min_threshold:units = "kJ/m/s or kW/s"; From e1a4dca6b3e4a1c59e862f92ce37a34f172fdfeb Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 13 Mar 2025 13:56:15 -0700 Subject: [PATCH 024/194] hist var dimension index bug fix --- main/FatesHistoryInterfaceMod.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 7bbe3e4ec7..c52a5213d4 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -4452,10 +4452,10 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_m5_si_scls(io_si,i_scls) = hio_m5_si_scls(io_si,i_scls) + & (sites(s)%fmort_rate_canopy(i_scls, ft) + & sites(s)%fmort_rate_ustory(i_scls, ft)) / m2_per_ha - hio_m12_si_scpf(io_si,i_scpf) = (sites(s)%rxfmort_rate_canopy(i_scls, i_pft) + & + hio_m12_si_scpf(io_si,i_scpf) = (sites(s)%rxfmort_rate_canopy(i_scls,ft) + & sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha hio_m12_si_scls(io_si,i_scls) = hio_m12_si_scls(io_si,i_scls) + & - (sites(s)%rxfmort_rate_canopy(i_scls, i_pft) + & + (sites(s)%rxfmort_rate_canopy(i_scls, ft) + & sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha ! hio_crownfiremort_si_scpf(io_si,i_scpf) = sites(s)%fmort_rate_crown(i_scls, ft) / m2_per_ha From 6d1e27d1f42060b7f55eec4c93967d57b0112d9a Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 13 Mar 2025 16:55:21 -0700 Subject: [PATCH 025/194] fix error in calculating burn window for his var --- main/FatesHistoryInterfaceMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 314ce4bb56..f1423d2f91 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -2549,7 +2549,7 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_effect_wspeed_si(io_si) = sites(s)%fireWeather%effective_windspeed/sec_per_min ! Prescribed fire burn window - hio_rx_burn_window_si(io_si) = hio_rx_burn_window_si(io_si) + sites(s)%rx_flag + hio_rx_burn_window_si(io_si) = hio_rx_burn_window_si(io_si) + sites(s)%fireWeather%rx_flag ! number of ignitions [#/km2/day -> #/m2/s] hio_fire_nignitions_si(io_si) = sites(s)%NF_successful / m2_per_km2 / & From f1d1d48cfd7a6c37957500841364e09f9e3ba1ab Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 13 Mar 2025 20:38:29 -0700 Subject: [PATCH 026/194] update burn window every time --- fire/SFFireWeatherMod.F90 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index b9178a53b1..6639030c9a 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -99,6 +99,8 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up if(t_check .le. 0.0_r8 .and. rh_check .le. 0.0_r8 .and. & ws_check .le. 0.0_r8)then this%rx_flag = 1 + else + this%rx_flag = 0 end if end subroutine UpdateRxfireBurnWindow From 383c1c191af1ee9c6ac0da4735594a934ccf6b06 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Fri, 14 Mar 2025 17:28:09 -0700 Subject: [PATCH 027/194] make min frac burnable to be user parameter --- fire/SFMainMod.F90 | 7 +++---- fire/SFParamsMod.F90 | 12 ++++++++++++ parameter_files/fates_params_default.cdl | 5 +++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index cdf146b71d..4d76577bae 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -541,6 +541,7 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) !if yes, calculate burned fraction as (user defined frac / total burnable frac) use SFParamsMod, only : SF_val_rxfire_AB !user defined prescribed fire area in fraction per day to reflect burning capacity + use SFParamsMod, only : SF_val_rxfire_min_frac ! minimum fraction of land needs to be burnable for conducting prescribed fire ! ARGUMENTS type(ed_site_type), intent(inout), target :: currentSite @@ -550,9 +551,7 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) real(r8) :: total_burnable_frac ! total fractional land area that can apply prescribed fire after condition checks at site level - ! Testing parameters - real(r8), parameter :: min_frac_site = 0.1_r8 - + ! initialize site variables currentSite%rxfire_area_final = 0.0_r8 total_burnable_frac = 0.0_r8 @@ -567,7 +566,7 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then currentPatch%rxfire_frac_burnt = 0.0_r8 if (currentPatch%rxfire .eq. itrue .and. & - total_burnable_frac .ge. min_frac_site ) then + total_burnable_frac .ge. SF_val_rxfire_min_frac ) then currentSite%rxfire_area_final = currentSite%rxfire_area_final + currentPatch%area ! the final burned total land area currentPatch%rxfire_frac_burnt = min(0.99_r8, (SF_val_rxfire_AB / total_burnable_frac)) else diff --git a/fire/SFParamsMod.F90 b/fire/SFParamsMod.F90 index 78af5a85c9..d6c8530c25 100644 --- a/fire/SFParamsMod.F90 +++ b/fire/SFParamsMod.F90 @@ -49,6 +49,7 @@ module SFParamsMod real(r8),protected, public :: SF_val_rxfire_maxthreshold ! maximum fire energy real(r8),protected, public :: SF_val_rxfire_fuel_min ! minimum fuel load at the patch for the need of rx fire management real(r8),protected, public :: SF_val_rxfire_fuel_max ! maximum fuel load, above which might be risky for conducting rx fire + real(r8),protected, public :: SF_val_rxfire_min_frac ! minimum fraction needs to be burnable at site level for conducting rx fire character(len=param_string_length),parameter :: SF_name_fdi_alpha = "fates_fire_fdi_alpha" character(len=param_string_length),parameter :: SF_name_miner_total = "fates_fire_miner_total" @@ -80,6 +81,8 @@ module SFParamsMod character(len=param_string_length),parameter :: SF_name_rxfire_max_threshold = "fates_rxfire_max_threshold" character(len=param_string_length),parameter :: SF_name_rxfire_fuel_min = "fates_rxfire_fuel_min" character(len=param_string_length),parameter :: SF_name_rxfire_fuel_max = "fates_rxfire_fuel_max" + character(len=param_string_length),parameter :: SF_name_rxfire_min_frac = "fates_rxfire_min_frac" + character(len=*), parameter, private :: sourcefile = __FILE__ @@ -192,6 +195,7 @@ subroutine SpitFireParamsInit() SF_val_rxfire_maxthreshold = nan SF_val_rxfire_fuel_min = nan SF_val_rxfire_fuel_max = nan + SF_val_rxfire_min_frac = nan end subroutine SpitFireParamsInit @@ -296,6 +300,11 @@ subroutine SpitFireRegisterScalars(fates_params) call fates_params%RegisterParameter(name=SF_name_rxfire_fuel_max, dimension_shape=dimension_shape_scalar, & dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_min_frac, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + end subroutine SpitFireRegisterScalars @@ -369,6 +378,9 @@ subroutine SpitFireReceiveScalars(fates_params) call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_max, & data=SF_val_rxfire_fuel_max) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_min_frac, & + data=SF_val_rxfire_min_frac) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 44a968232f..e53de33e35 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -936,6 +936,9 @@ variables: double fates_rxfire_fuel_max ; fates_rxfire_fuel_max:units = "kgC/m2"; fates_rxfire_fuel_max:long_name= "maximum fuel load above which prescribed fire can be risky"; + double fates_rxfire_min_frac ; + fates_rxfire_min_frac:units = "fraction"; + fates_rxfire_min_frac:long_name="minimum fraction of land needs to be burnable for conducting rx fire"; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; @@ -1898,6 +1901,8 @@ data: fates_rxfire_fuel_max = 1.5 ; + fates_rxfire_min_frac = 0.1 ; + fates_soil_salinity = 0.4 ; fates_trs_seedling2sap_par_timescale = 32 ; From e100d1eb0d52f8d4233a1232c382ecb10a5fcaa0 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 09:24:38 -0700 Subject: [PATCH 028/194] track Rx and Non-Rx fire separately with sum of the two included too --- biogeochem/EDCohortDynamicsMod.F90 | 7 +- biogeochem/EDPatchDynamicsMod.F90 | 114 +++++++++++++++++++---------- biogeochem/FatesCohortMod.F90 | 45 ++++++++---- biogeochem/FatesPatchMod.F90 | 30 +++++--- fire/SFMainMod.F90 | 98 ++++++++++++++++--------- main/EDTypesMod.F90 | 47 ++++++++---- 6 files changed, 224 insertions(+), 117 deletions(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 70b7057037..4cd6791c0d 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1020,8 +1020,11 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%fire_mort = (currentCohort%n*currentCohort%fire_mort + & nextc%n*nextc%fire_mort)/newn - currentCohort%rxfire_mort = (currentCohort%n*currentCohort%rxfire_mort + & - nextc%n*nextc%rxfire_mort)/newn + currentCohort%nonrx_mort = (currentCohort%n*currentCohort*nonrx_mort + & + nextc%n*nextc%nonrx_mort)/newn + + currentCohort%rx_mort = (currentCohort%n*currentCohort%rx_mort + & + nextc%n*nextc%rx_mort)/newn ! mortality diagnostics currentCohort%cmort = (currentCohort%n*currentCohort%cmort + nextc%n*nextc%cmort)/newn diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index eb75abe264..ddc47c8885 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -434,13 +434,12 @@ subroutine disturbance_rates( site_in, bc_in) endif ! Fire Disturbance Rate - currentPatch%disturbance_rates(dtype_ifire) = ( currentPatch%frac_burnt + currentPatch%rxfire_frac_burnt ) + currentPatch%disturbance_rates(dtype_ifire) = currentPatch%frac_burnt ! Fires can't burn the whole patch, as this causes /0 errors. if (currentPatch%disturbance_rates(dtype_ifire) > 0.98_r8)then - msg = 'very high fire areas'//trim(A2S(currentPatch%disturbance_rates(:)))//trim(N2S((currentPatch%frac_burnt + & - currentPatch%rxfire_frac_burnt))) + msg = 'very high fire areas'//trim(A2S(currentPatch%disturbance_rates(:)))//trim(N2S(currentPatch%frac_burnt)) call FatesWarn(msg,index=2) endif @@ -975,20 +974,29 @@ subroutine spawn_patches( currentSite, bc_in) ! due to fire, as well as from each fire mortality term currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%fire_mort / hlm_freq_day + nc%n * currentCohort%fire_mort / hlm_freq_day ! total + + currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_mort / hlm_freq_day ! for prescribed fire - currentSite%rxfmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & - currentSite%rxfmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rxfire_mort / hlm_freq_day ! for prescribed fire + currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_mort / hlm_freq_day ! for wildfire fire currentSite%fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%fmort_carbonflux_canopy(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 - currentSite%rxfmort_carbonflux_canopy(currentCohort%pft) = & - currentSite%rxfmort_carbonflux_canopy(currentCohort%pft) + & - (nc%n * currentCohort%rxfire_mort) * & + currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) = & + currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) + & + (nc%n * currentCohort%rx_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) = & + currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) + & + (nc%n * currentCohort%nonrx_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 else @@ -997,19 +1005,30 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%fire_mort / hlm_freq_day - currentSite%rxfmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & - currentSite%rxfmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rxfire_mort / hlm_freq_day + currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_mort / hlm_freq_day + + currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_mort / hlm_freq_day currentSite%fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%fmort_carbonflux_ustory(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 - currentSite%rxfmort_carbonflux_ustory(currentCohort%pft) = & - currentSite%rxfmort_carbonflux_ustory(currentCohort%pft) + & - (nc%n * currentCohort%rxfire_mort) * & + currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) = & + currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) + & + (nc%n * currentCohort%rx_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) = & + currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) + & + (nc%n * currentCohort%nonrx_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + + end if currentSite%fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & @@ -1019,13 +1038,19 @@ subroutine spawn_patches( currentSite, bc_in) leaf_c ) * & g_per_kg * days_per_sec * ha_per_m2 - currentSite%rxfmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & - currentSite%rxfmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & - (nc%n * currentCohort%rxfire_mort) * & + currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & + (nc%n * currentCohort%rx_mort) * & ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & leaf_c ) * & g_per_kg * days_per_sec * ha_per_m2 + currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & + (nc%n * currentCohort%nonrx_mort) * & + ((sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & + leaf_c) * g_per_kg * days_per_sec * ha_per_m2 + currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & @@ -1034,15 +1059,22 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%crownfire_mort / hlm_freq_day - currentSite%rxfmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & - currentSite%rxfmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rxcambial_mort / hlm_freq_day - currentSite%rxfmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & - currentSite%rxfmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rxcrownfire_mort / hlm_freq_day + currentSite%rx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_cambial_mort / hlm_freq_day + currentSite%rx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_crown_mort / hlm_freq_day + + currentSite%nonrx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_cambial_mort / hlm_freq_day + currentSite%nonrx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_crown_mort / hlm_freq_day ! loss of individual from fire in new patch. - nc%n = nc%n * (1.0_r8 - (currentCohort%fire_mort + currentCohort%rxfire_mort)) + nc%n = nc%n * (1.0_r8 - currentCohort%fire_mort) nc%cmort = currentCohort%cmort nc%hmort = currentCohort%hmort @@ -1074,15 +1106,18 @@ subroutine spawn_patches( currentSite, bc_in) if( (leaf_burn_frac < 0._r8) .or. & (leaf_burn_frac > 1._r8) .or. & - (currentCohort%fire_mort < 0._r8) .or. & - (currentCohort%fire_mort > 1._r8) .or. & - (currentCohort%rxfire_mort < 0._r8) .or. & - (currentCohort%rxfire_mort > 1._r8)) then + (currentCohort%fire_mort < 0._r8) .or. & + (currentCohort%fire_mort > 1._r8) .or. & + (currentCohort%rx_mort < 0._r8) .or. & + (currentCohort%rx_mort > 1._r8) .or. & + (currentCohort%nonrx_mort < 0._r8) .or. & + (currentCohort%nonrx_mort > 1._r8) ) then write(fates_log(),*) 'unexpected fire fractions' write(fates_log(),*) prt_params%woody(currentCohort%pft) write(fates_log(),*) leaf_burn_frac write(fates_log(),*) currentCohort%fire_mort - write(fates_log(),*) currentCohort%rxfire_mort + write(fates_log(),*) currentCohort%rx_mort + write(fates_log(),*) currentCohort%nonrx_mort call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -2012,8 +2047,7 @@ subroutine TransLitterNewPatch(currentSite, & do c = 1,ncwd frac_burnt = 0.0_r8 - if (dist_type == dtype_ifire .and. (currentPatch%fire == 1 .or. & - currentPatch%rxfire == 1)) then + if (dist_type == dtype_ifire .and. currentPatch%fire == 1) then frac_burnt = currentPatch%fuel%frac_burnt(c) end if @@ -2040,8 +2074,7 @@ subroutine TransLitterNewPatch(currentSite, & enddo frac_burnt = 0.0_r8 - if (dist_type == dtype_ifire .and. (currentPatch%fire == 1 .or. & - currentPatch%rxfire == 1)) then + if (dist_type == dtype_ifire .and. currentPatch%fire == 1) then frac_burnt = currentPatch%fuel%frac_burnt(fuel_classes%dead_leaves()) end if @@ -2168,14 +2201,14 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & !--------------------------------------------------------------------- ! Only do this if there was a fire in this actual patch. - if ( currentPatch%fire == ifalse .and. currentPatch%rxfire == ifalse ) return + if ( currentPatch%fire == ifalse ) return ! If plant hydraulics are turned on, account for water leaving the plant-soil ! mass balance through the dead trees if (hlm_use_planthydro == itrue) then currentCohort => currentPatch%shortest do while(associated(currentCohort)) - num_dead_trees = (( currentCohort%fire_mort + currentCohort%rxfire_mort)* & + num_dead_trees = (currentCohort%fire_mort * & currentCohort%n*patch_site_areadis/currentPatch%area) call AccumulateMortalityWaterStorage(currentSite,currentCohort,num_dead_trees) currentCohort => currentCohort%taller @@ -2249,7 +2282,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & ! Absolute number of dead trees being transfered in with the donated area - num_dead_trees = ((currentCohort%fire_mort + currentCohort%rxfire_mort) * & + num_dead_trees = (currentCohort%fire_mort * & currentCohort%n * patch_site_areadis/currentPatch%area) ! Contribution of dead trees to leaf litter @@ -3244,11 +3277,14 @@ subroutine fuse_2_patches(csite, dp, rp) rp%tau_l = (dp%tau_l*dp%area + rp%tau_l*rp%area) * inv_sum_area rp%tfc_ros = (dp%tfc_ros*dp%area + rp%tfc_ros*rp%area) * inv_sum_area rp%fi = (dp%fi*dp%area + rp%fi*rp%area) * inv_sum_area + rp%nonrx_fi = (dp%nonrx_fi*dp%area + rp%nonrx_fi*rp%area) * inv_sum_area + rp%rx_fi = (dp%rx_fi*dp%area + rp%rx_fi*rp%area) * inv_sum_area rp%fd = (dp%fd*dp%area + rp%fd*rp%area) * inv_sum_area rp%ros_back = (dp%ros_back*dp%area + rp%ros_back*rp%area) * inv_sum_area rp%scorch_ht(:) = (dp%scorch_ht(:)*dp%area + rp%scorch_ht(:)*rp%area) * inv_sum_area rp%frac_burnt = (dp%frac_burnt*dp%area + rp%frac_burnt*rp%area) * inv_sum_area - rp%rxfire_frac_burnt = (dp%rxfire_frac_burnt*dp%area + rp%rxfire_frac_burnt*rp%area) * inv_sum_area + rp%rx_frac_burnt = (dp%rx_frac_burnt*dp%area + rp%rx_frac_burnt*rp%area) * inv_sum_area + rp%nonrx_frac_burnt = (dp%nonrx_frac_burnt*dp%area + rp%nonrx_frac_burnt*rp%area) * inv_sum_area rp%btran_ft(:) = (dp%btran_ft(:)*dp%area + rp%btran_ft(:)*rp%area) * inv_sum_area rp%zstar = (dp%zstar*dp%area + rp%zstar*rp%area) * inv_sum_area rp%c_stomata = (dp%c_stomata*dp%area + rp%c_stomata*rp%area) * inv_sum_area diff --git a/biogeochem/FatesCohortMod.F90 b/biogeochem/FatesCohortMod.F90 index 28b0e98ae4..3f5073be1b 100644 --- a/biogeochem/FatesCohortMod.F90 +++ b/biogeochem/FatesCohortMod.F90 @@ -271,9 +271,12 @@ module FatesCohortMod real(r8) :: crownfire_mort ! probability of tree post-fire mortality from crown scorch [0-1] ! (conditional on the tree being subjected to the fire) real(r8) :: fire_mort ! post-fire mortality from cambial and crown damage assuming two are independent [0-1] - real(r8) :: rxcambial_mort ! cambial kill mortality due to prescribed fire - real(r8) :: rxcrownfire_mort ! crown fire mortality due to prescribed fire - real(r8) :: rxfire_mort ! post-fire mortality due to prescribed fire + real(r8) :: nonrx_cambial_mort ! cambial kill mortality due to wildfire + real(r8) :: nonrx_crown_mort ! crown fire mortality due to wildfire + real(r8) :: nonrx_mort ! post-fire mortality due to wildfire + real(r8) :: rx_cambial_mort ! cambial kill mortality due to prescribed fire + real(r8) :: rx_crown_mort ! crown fire mortality due to prescribed fire + real(r8) :: rx_mort ! post-fire mortality due to prescribed fire !--------------------------------------------------------------------------- @@ -452,9 +455,12 @@ subroutine NanValues(this) this%cambial_mort = nan this%crownfire_mort = nan this%fire_mort = nan - this%rxcambial_mort = nan - this%rxcrownfire_mort = nan - this%rxfire_mort = nan + this%nonrx_cambial_mort = nan + this%nonrx_crown_mort = nan + this%nonrx_mort = nan + this%rx_cambial_mort = nan + this%rx_crown_mort = nan + this%rx_mort = nan end subroutine NanValues @@ -541,9 +547,12 @@ subroutine ZeroValues(this) this%cambial_mort = 0._r8 this%crownfire_mort = 0._r8 this%fire_mort = 0._r8 - this%rxcambial_mort = 0._r8 - this%rxcrownfire_mort = 0._r8 - this%rxfire_mort = 0._r8 + this%nonrx_cambial_mort = 0._r8 + this%nonrx_crown_mort = 0._r8 + this%nonrx_mort = 0._r8 + this%rx_cambial_mort = 0._r8 + this%rx_crown_mort = 0._r8 + this%rx_mort = 0._r8 end subroutine ZeroValues @@ -789,9 +798,12 @@ subroutine Copy(this, copyCohort) copyCohort%cambial_mort = this%cambial_mort copyCohort%crownfire_mort = this%crownfire_mort copyCohort%fire_mort = this%fire_mort - copyCohort%rxcambial_mort = this%rxcambial_mort - copyCohort%rxcrownfire_mort = this%rxcrownfire_mort - copyCohort%rxfire_mort = this%rxfire_mort + copyCohort%nonrx_cambial_mort = this%nonrx_cambial_mort + copyCohort%nonrx_crown_mort = this%nonrx_crown_mort + copyCohort%nonrx_mort = this%nonrx_mort + copyCohort%rx_cambial_mort = this%rx_cambial_mort + copyCohort%rx_crown_mort = this%rx_crown_mort + copyCohort%rx_mort = this%rx_mort ! HYDRAULICS if (hlm_use_planthydro .eq. itrue) then @@ -1092,9 +1104,12 @@ subroutine Dump(this) write(fates_log(),*) 'cohort%fire_mort = ', this%fire_mort write(fates_log(),*) 'cohort%crownfire_mort = ', this%crownfire_mort write(fates_log(),*) 'cohort%cambial_mort = ', this%cambial_mort - write(fates_log(),*) 'cohort%rxcrownfire_mort = ', this%rxcrownfire_mort - write(fates_log(),*) 'cohort%rxcambial_mort = ', this%rxcambial_mort - write(fates_log(),*) 'cohort%rxfire_mort = ', this%rxfire_mort + write(fates_log(),*) 'cohort%nonrx_cambial_mort = ', this%nonrx_cambial_mort + write(fates_log(),*) 'cohort%nonrx_crown_mort = ', this%nonrx_crown_mort + write(fates_log(),*) 'cohort%nonrx_mort = ', this%nonrx_mort + write(fates_log(),*) 'cohort%rx_crown_mort = ', this%rx_crown_mort + write(fates_log(),*) 'cohort%rx_cambial_mort = ', this%rx_cambial_mort + write(fates_log(),*) 'cohort%rx_mort = ', this%rx_mort write(fates_log(),*) 'cohort%size_class = ', this%size_class write(fates_log(),*) 'cohort%size_by_pft_class = ', this%size_by_pft_class diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 3ef8e06c0d..0321edfafc 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -211,14 +211,19 @@ module FatesPatchMod real(r8) :: ros_back ! rate of backward spread of fire [m/min] real(r8) :: tau_l ! duration of lethal heating [min] real(r8) :: fi ! average fire intensity of flaming front [kJ/m/s] or [kW/m] - integer :: fire ! is there a fire? [1=yes; 0=no] + integer :: fire ! is there a fire (rx + nonrx)? [1=yes; 0=no] real(r8) :: fd ! fire duration [min] - real(r8) :: frac_burnt ! fraction of patch burnt by fire + real(r8) :: frac_burnt ! total fraction of patch burnt by fire (rx + nonrx) + + ! wildfire + real(r8) :: nonrx_fire ! is there a wildfire [1=yes; 0=no] + real(r8) :: nonrx_fi ! average fire intensity of wildfire flaming front + real(r8) :: nonrx_frac_burnt ! fraction burnt by wildfire ! prescribed fire - integer :: rxfire ! is there a prescribed fire? [1=yes; 0=no]; - real(r8) :: rxfire_fi ! average fire intensity of prescribed fire flaming front - real(r8) :: rxfire_frac_burnt ! fraction burnt by prescribed fire, it's user defined at patch level per fire event + integer :: rx_fire ! is there a prescribed fire? [1=yes; 0=no] + real(r8) :: rx_fi ! average fire intensity of prescribed fire flaming front + real(r8) :: rx_frac_burnt ! fraction burnt by prescribed fire, it's user defined at patch level per fire event ! fire effects real(r8) :: scorch_ht(maxpft) ! scorch height [m] @@ -512,9 +517,12 @@ subroutine NanValues(this) this%tau_l = nan this%fi = nan this%fire = fates_unset_int - this%rxfire = fates_unset_int - this%rxfire_fi = nan - this%rxfire_frac_burnt = nan + this%nonrx_fire = fates_unset_int + this%rx_fire = fates_unset_int + this%nonrx_fi = nan + this%nonrx_frac_burnt = nan + this%rx_fi = nan + this%rx_frac_burnt = nan this%fd = nan this%scorch_ht(:) = nan this%tfc_ros = nan @@ -606,8 +614,10 @@ subroutine ZeroValues(this) this%scorch_ht(:) = 0.0_r8 this%tfc_ros = 0.0_r8 this%frac_burnt = 0.0_r8 - this%rxfire_fi = 0.0_r8 - this%rxfire_frac_burnt = 0.0_r8 + this%nonrx_fi = 0.0_r8 + this%nonrx_frac_burnt = 0.0_r8 + this%rx_fi = 0.0_r8 + this%rx_frac_burnt = 0.0_r8 end subroutine ZeroValues diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 4d76577bae..c28c51870c 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -10,6 +10,8 @@ module SFMainMod use FatesConstantsMod, only : pi_const use FatesConstantsMod, only : nocomp_bareground, nearzero use FatesGlobals, only : fates_log + use FatesGlobals , only : endrun => fates_endrun + use shr_log_mod , only : errMsg => shr_log_errMsg use FatesInterfaceTypesMod, only : hlm_masterproc use FatesInterfaceTypesMod, only : hlm_spitfire_mode use FatesInterfaceTypesMod, only : hlm_sf_nofire_def @@ -33,10 +35,14 @@ module SFMainMod use FatesInterfaceTypesMod, only : numpft use FatesAllometryMod, only : CrownDepth use FatesFuelClassesMod, only : fuel_classes + implicit none private + character(len=*), parameter, private :: sourcefile = & + __FILE__ + public :: DailyFireModel public :: UpdateFuelCharacteristics @@ -387,7 +393,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) - currentPatch%fuel%frac_burnt(:) = 0.0_r8 + currentPatch%fuel%frac_burnt(:) = 0.0_r8 if (currentPatch%nocomp_pft_label /= nocomp_bareground) then @@ -399,10 +405,11 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch%TFC_ROS = sum(fuel_consumed) - fuel_consumed(fuel_classes%trunks()) ! initialize patch parameters to zero - currentPatch%FI = 0.0_r8 - currentPatch%fire = 0 - currentPatch%rxfire = 0 - currentPatch%rxfire_FI = 0.0_r8 + currentPatch%FI = 0.0_r8 ! either nonrx or rx FI + currentPatch%nonrx_fire = 0 ! only wildfire + currentPatch%rx_fire = 0 ! only rx fire + currentPatch%rx_FI = 0.0_r8 + currentPatch%nonrx_FI = 0.0_r8 if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag .eq. itrue) then @@ -441,24 +448,25 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentSite%rxfire_area_fuel = currentSite%rxfire_area_fuel + currentPatch%area ! record burnable area after fuel load check if (is_rxfire) then currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check - currentPatch%rxfire = 1 + currentPatch%rx_fire = 1 else if (is_wildfire) then - currentPatch%fire = 1 + currentPatch%nonrx_fire = 1 end if else ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on ! track wildfires greater than kW/m energy threshold if (currentPatch%FI > SF_val_fire_threshold) then - currentPatch%fire = 1 + currentPatch%nonrx_fire = 1 end if end if - if (currentPatch%fire == itrue) then + if (currentPatch%nonrx_fire == itrue) then currentSite%NF_successful = currentSite%NF_successful + & currentSite%NF*currentSite%FDI*currentPatch%area/area - else if (currentPatch%rxfire == itrue) then - currentPatch%rxfire_FI = currentPatch%FI + currentPatch%nonrx_FI = currentPatch%FI + else if (currentPatch%rx_fire == itrue) then + currentPatch%rx_FI = currentPatch%FI end if end if @@ -479,7 +487,6 @@ subroutine CalculateAreaBurnt(currentSite) use FatesConstantsMod, only : m2_per_km2 use SFEquationsMod, only : FireDuration, LengthToBreadth use SFEquationsMod, only : AreaBurnt, FireSize - use SFParamsMod, only : SF_val_fire_threshold ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -501,9 +508,9 @@ subroutine CalculateAreaBurnt(currentSite) ! initialize patch parameters to zero currentPatch%FD = 0.0_r8 - currentPatch%frac_burnt = 0.0_r8 + currentPatch%nonrx_frac_burnt = 0.0_r8 - if (currentSite%NF > 0.0_r8 .and. currentPatch%FI > SF_val_fire_threshold) then + if (currentPatch%nonrx_fire == 1) then ! fire duration [min] currentPatch%FD = FireDuration(currentSite%FDI) @@ -521,7 +528,7 @@ subroutine CalculateAreaBurnt(currentSite) ! convert to area burned per area patch per day ! i.e., fraction of the patch burned on that day - currentPatch%frac_burnt = min(max_frac_burnt, area_burnt/m2_per_km2) + currentPatch%nonrx_frac_burnt = min(max_frac_burnt, area_burnt/m2_per_km2) end if end if @@ -564,15 +571,31 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) do while(associated(currentPatch)) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - currentPatch%rxfire_frac_burnt = 0.0_r8 - if (currentPatch%rxfire .eq. itrue .and. & + currentPatch%fire = 0 ! fire, either rx or non-rx + currentPatch%frac_burnt = 0.0_r8 ! rx_frac_burnt + nonrx_frac_burnt + currentPatch%rx_frac_burnt = 0.0_r8 + if (currentPatch%rx_fire .eq. itrue .and. & total_burnable_frac .ge. SF_val_rxfire_min_frac ) then currentSite%rxfire_area_final = currentSite%rxfire_area_final + currentPatch%area ! the final burned total land area - currentPatch%rxfire_frac_burnt = min(0.99_r8, (SF_val_rxfire_AB / total_burnable_frac)) + currentPatch%rx_frac_burnt = min(0.99_r8, (SF_val_rxfire_AB / total_burnable_frac)) else - currentPatch%rxfire = 0 ! update rxfire occurence at patch - currentPatch%rxfire_FI = 0.0_r8 + currentPatch%rx_fire = 0 ! update rxfire occurence at patch + currentPatch%rx_FI = 0.0_r8 + end if + ! update patch level fire occurence and total frac burnt + currentPatch%fire = currentPatch%nonrx_fire + currentPatch%rx_fire + currentPatch%frac_burnt = currentPatch%nonrx_frac_burnt + currentPatch%rx_frac_burnt + + ! currentPatch%fire cannot be >1, which indicates both rx and wildfire are happening + ! we currently do not allow this to happen on the same patch yet + if (currentPatch%fire > 1) then + write(fates_log(),*) 'Both wildfire and management fire are happening at same patch' + write(fates_log(),*) 'rxfire =',currentPatch%rx_fire + write(fates_log(),*) 'wildfire =',currentPatch%nonrx_fire + call endrun(msg=errMsg(sourcefile, __LINE__)) end if + + end if currentPatch => currentPatch%younger; @@ -610,7 +633,7 @@ subroutine crown_scorching ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then tree_ag_biomass = 0.0_r8 - if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then + if (currentPatch%fire == 1) then currentCohort => currentPatch%tallest; do while(associated(currentCohort)) if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only @@ -666,7 +689,7 @@ subroutine crown_damage ( currentSite ) do while(associated(currentPatch)) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then + if (currentPatch%fire == 1) then currentCohort=>currentPatch%tallest @@ -736,7 +759,7 @@ subroutine cambial_damage_kill ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then + if (currentPatch%fire == 1) then currentCohort => currentPatch%tallest; do while(associated(currentCohort)) if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only @@ -789,14 +812,18 @@ subroutine post_fire_mortality ( currentSite ) if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1 .or. currentPatch%rxfire == 1) then + if (currentPatch%fire == 1) then currentCohort => currentPatch%tallest do while(associated(currentCohort)) - currentCohort%fire_mort = 0.0_r8 + currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 - currentCohort%rxfire_mort = 0.0_r8 - currentCohort%rxcrownfire_mort = 0.0_r8 - currentCohort%rxcambial_mort = 0.0_r8 + currentCohort%nonrx_mort = 0.0_r8 + currentCohort%nonrx_crown_mort = 0.0_r8 + currentCohort%nonrx_cambial_mort = 0.0_r8 + currentCohort%rx_mort = 0.0_r8 + currentCohort%rx_crown_mort = 0.0_r8 + currentCohort%rx_cambial_mort = 0.0_r8 + if ( prt_params%woody(currentCohort%pft) == itrue) then ! Equation 22 in Thonicke et al. 2010. currentCohort%crownfire_mort = EDPftvarcon_inst%crown_kill(currentCohort%pft)*currentCohort%fraction_crown_burned**3.0_r8 @@ -808,13 +835,14 @@ subroutine post_fire_mortality ( currentSite ) endif !trees ! now decide which type of post-fire mortality, prescribed fire or wildfire? - if (currentPatch%rxfire == itrue .and. currentPatch%fire == ifalse) then - currentCohort%rxfire_mort = currentCohort%fire_mort - currentCohort%rxcrownfire_mort = currentCohort%crownfire_mort - currentCohort%rxcambial_mort = currentCohort%cambial_mort - currentCohort%fire_mort = 0.0_r8 - currentCohort%crownfire_mort = 0.0_r8 - currentCohort%cambial_mort = 0.0_r8 + if (currentPatch%nonrx_fire == itrue .and. currentPatch%rx_fire == ifalse) then + currentCohort%nonrx_mort = currentCohort%fire_mort + currentCohort%nonrx_crown_mort = currentCohort%crownfire_mort + currentCohort%nonrx_cambial_mort = currentCohort%cambial_mort + else + currentCohort%rx_mort = currentCohort%fire_mort + currentCohort%rx_crown_mort = currentCohort%crownfire_mort + currentCohort%rx_cambial_mort = currentCohort%cambial_mort end if currentCohort => currentCohort%shorter diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index ead5b3c8f7..7878932cfd 100644 --- a/main/EDTypesMod.F90 +++ b/main/EDTypesMod.F90 @@ -499,9 +499,12 @@ module EDTypesMod real(r8) :: fmort_crownarea_canopy ! crownarea of canopy indivs killed due to fire per year. [m2/sec] real(r8) :: fmort_crownarea_ustory ! crownarea of understory indivs killed due to fire per year [m2/sec] + real(r8) :: rx_fmort_crownarea_canopy ! crownarea of canopy indivs killed due to precribed fire per year [m2/sec] + real(r8) :: rx_fmort_crownarea_ustory ! crownarea of undertsory indivs killed due to prescribed fire per year [m2/sec] + real(r8) :: nonrx_fmort_crownarea_canopy ! crownarea of canopy indivs killed due to wildfire per year [m2/sec] + real(r8) :: nonrx_fmort_crownarea_ustory ! crownarea of understory indivs killed due to wildfire per year [m2/sec] - real(r8) :: rxfmort_crownarea_canopy ! crownarea of canopy indivs killed due to precribed fire per year [m2/sec] - real(r8) :: rxfmort_crownarea_ustory ! crownarea of undertsory indivs killed due to prescribed fire per year [m2/sec] + real(r8), allocatable :: term_nindivs_canopy(:,:,:) ! number of canopy individuals that were in cohorts which ! were terminated this timestep, by termination type, size x pft @@ -515,13 +518,16 @@ module EDTypesMod real(r8), allocatable :: imort_carbonflux(:) ! biomass of individuals killed due to impact mortality per year, by pft. [kgC/m2/sec] real(r8), allocatable :: fmort_carbonflux_canopy(:) ! biomass of canopy indivs killed due to fire per year, by pft. [gC/m2/sec] real(r8), allocatable :: fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to fire per year, by pft [gC/m2/sec] - real(r8), allocatable :: rxfmort_carbonflux_canopy(:) ! biomass of cnaopy indivs killed due to prescribed fire per year [gC/m2/sec] - real(r8), allocatable :: rxfmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to prescribed fire per year [gC/m2/sec] + real(r8), allocatable :: rx_fmort_carbonflux_canopy(:) ! biomass of cnaopy indivs killed due to prescribed fire per year [gC/m2/sec] + real(r8), allocatable :: rx_fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to prescribed fire per year [gC/m2/sec] + real(r8), allocatable :: nonrx_fmort_carbonflux_canopy(:) ! biomass of canopy indivs killed due to wildfire per year [gC/m2/sec] + real(r8), allocatable :: nonrx_fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to wildfire per year [gC/m2/sec] - real(r8), allocatable :: term_abg_flux(:,:) ! aboveground biomass lost due to termination mortality x size x pft - real(r8), allocatable :: imort_abg_flux(:,:) ! aboveground biomass lost due to impact mortality x size x pft [kgC/m2/sec] - real(r8), allocatable :: fmort_abg_flux(:,:) ! aboveground biomass lost due to fire mortality x size x pft - real(r8), allocatable :: rxfmort_abg_flux(:,:) ! aboveground biomass loss due to precribed fire mortality x size x pft + real(r8), allocatable :: term_abg_flux(:,:) ! aboveground biomass lost due to termination mortality x size x pft + real(r8), allocatable :: imort_abg_flux(:,:) ! aboveground biomass lost due to impact mortality x size x pft [kgC/m2/sec] + real(r8), allocatable :: fmort_abg_flux(:,:) ! aboveground biomass lost due to total fire mortality x size x pft + real(r8), allocatable :: rx_fmort_abg_flux(:,:) ! aboveground biomass loss due to precribed fire mortality x size x pft + real(r8), allocatable :: nonrx_fmort_abg_flux(:,:) ! aboveground biomass loss due to wildfire mortality x size x pft real(r8) :: demotion_carbonflux ! biomass of demoted individuals from canopy to understory [kgC/ha/day] @@ -542,10 +548,15 @@ module EDTypesMod real(r8), allocatable :: fmort_rate_crown(:,:) ! rate of individuals killed due to fire mortality ! from crown damage per year. on size x pft array - real(r8), allocatable :: rxfmort_rate_canopy(:,:) ! rate of canopy individuals killed due to prescribed fire per year - real(r8), allocatable :: rxfmort_rate_ustory(:,:) ! rate of understory individuals killed due to precribed fire per yr - real(r8), allocatable :: rxfmort_rate_cambial(:,:) ! cambial mortality rate due to prescribed fire - real(r8), allocatable :: rxfmort_rate_crown(:,:) ! crown damage mortality due to prescribed fire + real(r8), allocatable :: rx_fmort_rate_canopy(:,:) ! rate of canopy individuals killed due to prescribed fire per year + real(r8), allocatable :: rx_fmort_rate_ustory(:,:) ! rate of understory individuals killed due to precribed fire per yr + real(r8), allocatable :: rx_fmort_rate_cambial(:,:) ! cambial mortality rate due to prescribed fire + real(r8), allocatable :: rx_fmort_rate_crown(:,:) ! crown damage mortality due to prescribed fire + + real(r8), allocatable :: nonrx_fmort_rate_canopy(:,:) ! rate of canopy indivs killed due to wildfire per year + real(r8), allocatable :: nonrx_fmort_rate_ustory(:,:) ! rate of understory indivs killed due to wildfire per year + real(r8), allocatable :: nonrx_fmort_rate_cambial(:,:) ! cambial mortality rate due to wildfire + real(r8), allocatable :: nonrx_fmort_rate_crown(:,:) ! crown damage mortality due to wildfire real(r8), allocatable :: imort_rate_damage(:,:,:) ! number of individuals per damage class that die from impact mortality real(r8), allocatable :: term_nindivs_canopy_damage(:,:,:) ! number of individuals per damage class that die from termination mortality - canopy @@ -554,10 +565,14 @@ module EDTypesMod real(r8), allocatable :: fmort_rate_ustory_damage(:,:,:) ! number of individuals per damage class that die from fire - ustory real(r8), allocatable :: fmort_cflux_canopy_damage(:,:) ! cflux per damage class that die from fire - canopy real(r8), allocatable :: fmort_cflux_ustory_damage(:,:) ! cflux per damage class that die from fire - ustory - real(r8), allocatable :: rxfmort_rate_canopy_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -canopy - real(r8), allocatable :: rxfmort_rate_ustory_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -understory - real(r8), allocatable :: rxfmort_cflux_canopy_damage(:,:) ! cflux per damage class that die from prescribed fire -canopy - real(r8), allocatable :: rxfmort_cflux_ustory_damage(:,:) ! cflux per damage class that die from precribed fire - understory + real(r8), allocatable :: rx_fmort_rate_canopy_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -canopy + real(r8), allocatable :: rx_fmort_rate_ustory_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -understory + real(r8), allocatable :: rx_fmort_cflux_canopy_damage(:,:) ! cflux per damage class that die from prescribed fire -canopy + real(r8), allocatable :: rx_fmort_cflux_ustory_damage(:,:) ! cflux per damage class that die from precribed fire - understory + real(r8), allocatable :: nonrx_fmort_rate_canopy_damage(:,:,:) !number of indivs per damage class that die from wildfire -canopy + real(r8), allocatable :: nonrx_fmort_rate_ustory_damage(:,:,:) !number of indivs per damage class that die from wildfire -understory + real(r8), allocatable :: nonrx_fmort_cflux_canopy_damage(:,:) !cflux per damage class that die from wildfire - canopy + real(r8), allocatable :: nonrx_fmort_cflux_ustory_damage(:,:) !cflux per damage class that die from wildfire - understory real(r8), allocatable :: imort_cflux_damage(:,:) ! carbon flux from impact mortality by damage class [kgC/m2/sec] real(r8), allocatable :: term_cflux_canopy_damage(:,:) ! carbon flux from termination mortality by damage class real(r8), allocatable :: term_cflux_ustory_damage(:,:) ! carbon flux from termination mortality by damage class From c82e45c9b076383f1cb2c91a596a6838525fb81e Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 09:25:36 -0700 Subject: [PATCH 029/194] rename variables for rx and wildfire --- main/FatesRestartInterfaceMod.F90 | 422 +++++++++++++++--------------- 1 file changed, 211 insertions(+), 211 deletions(-) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index ed015047fd..e10487dca0 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -240,15 +240,15 @@ module FatesRestartInterfaceMod integer :: ir_recrate_sift integer :: ir_use_this_pft_sift integer :: ir_area_pft_sift - integer :: ir_fmortrate_cano_siscpf - integer :: ir_fmortrate_usto_siscpf - integer :: ir_rxfmortrate_cano_siscpf - integer :: ir_rxfmortrate_usto_siscpf + integer :: ir_nonrx_fmortrate_cano_siscpf + integer :: ir_nonrx_fmortrate_usto_siscpf + integer :: ir_rx_fmortrate_cano_siscpf + integer :: ir_rx_fmortrate_usto_siscpf integer :: ir_imortrate_siscpf - integer :: ir_fmortrate_crown_siscpf - integer :: ir_fmortrate_cambi_siscpf - integer :: ir_rxfmortrate_crown_siscpf - integer :: ir_rxfmortrate_cambi_siscpf + integer :: ir_nonrx_fmortrate_crown_siscpf + integer :: ir_nonrx_fmortrate_cambi_siscpf + integer :: ir_rx_fmortrate_crown_siscpf + integer :: ir_rx_fmortrate_cambi_siscpf integer :: ir_termnindiv_cano_siscpf integer :: ir_termnindiv_usto_siscpf integer :: ir_growflx_fusion_siscpf @@ -258,23 +258,23 @@ module FatesRestartInterfaceMod integer :: ir_termcarea_usto_si integer :: ir_imortcarea_si - integer :: ir_fmortcarea_cano_si - integer :: ir_fmortcarea_usto_si - integer :: ir_rxfmortcarea_cano_si - integer :: ir_rxfmortcarea_usto_si + integer :: ir_nonrx_fmortcarea_cano_si + integer :: ir_nonrx_fmortcarea_usto_si + integer :: ir_rx_fmortcarea_cano_si + integer :: ir_rx_fmortcarea_usto_si integer :: ir_termcflux_cano_sipft integer :: ir_termcflux_usto_sipft integer :: ir_democflux_si integer :: ir_promcflux_si integer :: ir_imortcflux_sipft - integer :: ir_fmortcflux_cano_sipft - integer :: ir_fmortcflux_usto_sipft - integer :: ir_rxfmortcflux_cano_sipft - integer :: ir_rxfmortcflux_usto_sipft + integer :: ir_nonrx_fmortcflux_cano_sipft + integer :: ir_nonrx_fmortcflux_usto_sipft + integer :: ir_rx_fmortcflux_cano_sipft + integer :: ir_rx_fmortcflux_usto_sipft integer :: ir_abg_term_flux_siscpf integer :: ir_abg_imort_flux_siscpf - integer :: ir_abg_fmort_flux_siscpf - integer :: ir_abg_rxfmort_flux_siscpf + integer :: ir_abg_nonrx_fmort_flux_siscpf + integer :: ir_abg_rx_fmort_flux_siscpf integer :: ir_disturbance_rates_siluludi @@ -300,17 +300,17 @@ module FatesRestartInterfaceMod integer :: ir_imortrate_sicdpf integer :: ir_termnindiv_cano_sicdpf integer :: ir_termnindiv_usto_sicdpf - integer :: ir_fmortrate_cano_sicdpf - integer :: ir_fmortrate_usto_sicdpf - integer :: ir_rxfmortrate_cano_sicdpf - integer :: ir_rxfmortrate_usto_sicdpf + integer :: ir_nonrx_fmortrate_cano_sicdpf + integer :: ir_nonrx_fmortrate_usto_sicdpf + integer :: ir_rx_fmortrate_cano_sicdpf + integer :: ir_rx_fmortrate_usto_sicdpf integer :: ir_imortcflux_sicdsc integer :: ir_termcflux_cano_sicdsc integer :: ir_termcflux_usto_sicdsc - integer :: ir_fmortcflux_cano_sicdsc - integer :: ir_fmortcflux_usto_sicdsc - integer :: ir_rxfmortcflux_cano_sicdsc - integer :: ir_rxfmortcflux_usto_sicdsc + integer :: ir_nonrx_fmortcflux_cano_sicdsc + integer :: ir_nonrx_fmortcflux_usto_sicdsc + integer :: ir_rx_fmortcflux_cano_sicdsc + integer :: ir_rx_fmortcflux_usto_sicdsc integer :: ir_crownarea_cano_si integer :: ir_crownarea_usto_si integer :: ir_emanpp_si @@ -1390,50 +1390,50 @@ subroutine define_restart_vars(this, initialize_variables) units='kg', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_seed_out_sift ) - call this%set_restart_var(vname='fates_fmortrate_canopy', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality canopy', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_canopy', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality canopy', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cano_siscpf) - call this%set_restart_var(vname='fates_fmortrate_ustory', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality ustory', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_ustory', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality ustory', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_usto_siscpf) - call this%set_restart_var(vname='fates_rxfmortrate_canopy', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_canopy', vtype=cohort_r8, & long_name='fates diagnostics on rx fire mortality canopy', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cano_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cano_siscpf) - call this%set_restart_var(vname='fates_rxfmortrate_ustory', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_ustory', vtype=cohort_r8, & long_name='fates diagnostics on rx fire mortality ustory', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_usto_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_usto_siscpf) call this%set_restart_var(vname='fates_imortrate', vtype=cohort_r8, & long_name='fates diagnostics on impact mortality', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortrate_siscpf) - call this%set_restart_var(vname='fates_fmortrate_crown', vtype=cohort_r8, & - long_name='fates diagnostics on crown fire mortality', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_crown', vtype=cohort_r8, & + long_name='fates diagnostics on crown fire mortality for wildfire', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_crown_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_crown_siscpf) - call this%set_restart_var(vname='fates_fmortrate_cambi', vtype=cohort_r8, & - long_name='fates diagnostics on fire cambial mortality', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_cambi', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire cambial mortality', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cambi_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cambi_siscpf) - call this%set_restart_var(vname='fates_rxfmortrate_crown', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_crown', vtype=cohort_r8, & long_name='fates diagnostics on rx fire crown fire mortality', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_crown_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_crown_siscpf) - call this%set_restart_var(vname='fates_rxfmortrate_cambi', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_cambi', vtype=cohort_r8, & long_name='fates diagnostics on rx fire cambial mortality', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cambi_siscpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cambi_siscpf) call this%set_restart_var(vname='fates_termn_canopy', vtype=cohort_r8, & long_name='fates diagnostics on termin mortality canopy', & @@ -1465,97 +1465,97 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcflux_sipft) - call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & + call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & long_name='crownarea of indivs killed due to impact mort', & units='m2/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcarea_si) - call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & - long_name='fates diagnostic biomass of canopy fire', & + call this%set_restart_var(vname='fates_nonrx_fmortcflux_canopy', vtype=cohort_r8, & + long_name='fates diagnostic biomass of canopy wildfire', & units='gC/m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sipft) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_cano_sipft) - call this%set_restart_var(vname='fates_fmortcflux_ustory', vtype=cohort_r8, & - long_name='fates diagnostic biomass of understory fire', & + call this%set_restart_var(vname='fates_nonrx_fmortcflux_ustory', vtype=cohort_r8, & + long_name='fates diagnostic biomass of understory wildfire', & units='gC/m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sipft) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_usto_sipft) - call this%set_restart_var(vname='fates_rxfmortcflux_canopy', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic biomass of canopy rx fire', & units='gC/m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_cano_sipft) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_cano_sipft) - call this%set_restart_var(vname='fates_rxfmortcflux_ustory', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortcflux_ustory', vtype=cohort_r8, & long_name='fates diagnostic biomass of understory rx fire', & units='gC/m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_usto_sipft) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_usto_sipft) call this%set_restart_var(vname='fates_termcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux canopy', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_cano_sipft ) - call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux understory', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_usto_sipft ) - call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from termination mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_term_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from impact mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_imort_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & - long_name='fates aboveground biomass loss from fire mortality', & + call this%set_restart_var(vname='fates_abg_nonrx_fmort_flux', vtype=cohort_r8, & + long_name='fates aboveground biomass loss from wildfire mortality', & units='', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_fmort_flux_siscpf ) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_nonrx_fmort_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_rxfmort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_rx_fmort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from rx fire mortality', & units='', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_rxfmort_flux_siscpf ) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_rx_fmort_flux_siscpf ) - call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & long_name='fates diagnostic demotion carbon flux', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_democflux_si ) - call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & long_name='fates diagnostic promotion carbon flux ', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_promcflux_si ) - call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & - long_name='fates diagnostic crownarea of canopy fire', & + call this%set_restart_var(vname='fates_nonrx_fmortcarea_canopy', vtype=site_r8, & + long_name='fates diagnostic crownarea of canopy wildfire', & units='m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_cano_si) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcarea_cano_si) - call this%set_restart_var(vname='fates_fmortcarea_ustory', vtype=site_r8, & - long_name='fates diagnostic crownarea of understory fire', & + call this%set_restart_var(vname='fates_nonrx_fmortcarea_ustory', vtype=site_r8, & + long_name='fates diagnostic crownarea of understory wildfire', & units='m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_usto_si) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcarea_usto_si) - call this%set_restart_var(vname='fates_rxfmortcarea_canopy', vtype=site_r8, & + call this%set_restart_var(vname='fates_rx_fmortcarea_canopy', vtype=site_r8, & long_name='fates diagnostic crownarea of canopy rx fire', & units='m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcarea_cano_si) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcarea_cano_si) - call this%set_restart_var(vname='fates_rxfmortcarea_ustory', vtype=site_r8, & + call this%set_restart_var(vname='fates_rx_fmortcarea_ustory', vtype=site_r8, & long_name='fates diagnostic crownarea of understory rx fire', & units='m2/sec', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcarea_usto_si) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcarea_usto_si) call this%set_restart_var(vname='fates_termcarea_canopy', vtype=site_r8, & long_name='fates diagnostic term crownarea canopy', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcarea_cano_si ) - call this%set_restart_var(vname='fates_termcarea_ustory', vtype=site_r8, & + call this%set_restart_var(vname='fates_termcarea_ustory', vtype=site_r8, & long_name='fates diagnostic term crownarea understory', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcarea_usto_si ) @@ -1576,25 +1576,25 @@ subroutine define_restart_vars(this, initialize_variables) units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termnindiv_usto_sicdpf) - call this%set_restart_var(vname='fates_fmortrate_cano_dam', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality by damage class', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_cano_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_sicdpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cano_sicdpf) - call this%set_restart_var(vname='fates_fmortrate_usto_dam', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality by damage class', & + call this%set_restart_var(vname='fates_nonrx_fmortrate_usto_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_sicdpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_usto_sicdpf) - call this%set_restart_var(vname='fates_rxfmortrate_cano_dam', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_cano_dam', vtype=cohort_r8, & long_name='fates diagnostics on rx fire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_cano_sicdpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cano_sicdpf) - call this%set_restart_var(vname='fates_rxfmortrate_usto_dam', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortrate_usto_dam', vtype=cohort_r8, & long_name='fates diagnostics on rx fire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortrate_usto_sicdpf) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_usto_sicdpf) call this%set_restart_var(vname='fates_imortcflux_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to impact mort by damage class', & @@ -1611,25 +1611,25 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_usto_sicdsc) - call this%set_restart_var(vname='fates_fmortcflux_cano_dam', vtype=cohort_r8, & - long_name='biomass of indivs killed due to fire mort by damage class', & + call this%set_restart_var(vname='fates_nonrx_fmortcflux_cano_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to wildfire mort by damage class', & units='kgC/ha/day', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sicdsc) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_cano_sicdsc) - call this%set_restart_var(vname='fates_fmortcflux_usto_dam', vtype=cohort_r8, & - long_name='biomass of indivs killed due to fire mort by damage class', & + call this%set_restart_var(vname='fates_nonrx_fmortcflux_usto_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to wildfire mort by damage class', & units='kgC/ha/day', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sicdsc) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_usto_sicdsc) - call this%set_restart_var(vname='fates_rxfmortcflux_cano_dam', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortcflux_cano_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to rx fire mort by damage class', & units='kgC/ha/day', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_cano_sicdsc) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_cano_sicdsc) - call this%set_restart_var(vname='fates_rxfmortcflux_usto_dam', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_rx_fmortcflux_usto_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to rx fire mort by damage class', & units='kgC/ha/day', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rxfmortcflux_usto_sicdsc) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_usto_sicdsc) call this%set_restart_var(vname='fates_crownarea_canopy_damage', vtype=site_r8, & long_name='fates area lost from damage each year', & @@ -1646,15 +1646,15 @@ subroutine define_restart_vars(this, initialize_variables) units='kg/m2/yr', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_emanpp_si) - call this%DefineRMeanRestartVar(vname='fates_tveg24patch',vtype=cohort_r8, & - long_name='24-hour patch veg temp', & - units='K', initialize=initialize_variables,ivar=ivar, index = ir_tveg24_pa) + call this%DefineRMeanRestartVar(vname='fates_tveg24patch',vtype=cohort_r8, & + long_name='24-hour patch veg temp', & + units='K', initialize=initialize_variables,ivar=ivar, index = ir_tveg24_pa) - call this%DefineRMeanRestartVar(vname='fates_disturbance_rates',vtype=cohort_r8, & - long_name='disturbance rates by donor land-use type, receiver land-use type, and disturbance type', & - units='1/day', initialize=initialize_variables,ivar=ivar, index = ir_disturbance_rates_siluludi) + call this%DefineRMeanRestartVar(vname='fates_disturbance_rates',vtype=cohort_r8, & + long_name='disturbance rates by donor land-use type, receiver land-use type, and disturbance type', & + units='1/day', initialize=initialize_variables,ivar=ivar, index = ir_disturbance_rates_siluludi) - if ( hlm_regeneration_model == TRS_regeneration ) then + if ( hlm_regeneration_model == TRS_regeneration ) then call this%DefineRMeanRestartVar(vname='fates_seedling_layer_par24',vtype=cohort_r8, & long_name='24-hour seedling layer PAR', & @@ -1675,8 +1675,8 @@ subroutine define_restart_vars(this, initialize_variables) call this%DefineRMeanRestartVar(vname='fates_sdlng_mdd',vtype=cohort_r8, & long_name='seedling moisture deficit days', & units='mm days', initialize=initialize_variables,ivar=ivar, index = ir_sdlng_mdd_pa) - - end if + + end if call this%DefineRMeanRestartVar(vname='fates_tveglpapatch',vtype=cohort_r8, & long_name='running average (EMA) of patch veg temp for photo acclim', & @@ -2268,15 +2268,15 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_area_pft_sift => this%rvars(ir_area_pft_sift)%r81d, & rio_seed_in_sift => this%rvars(ir_seed_in_sift)%r81d, & rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & - rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & - rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & - rio_rxfmortrate_cano_siscpf => this%rvars(ir_rxfmortrate_cano_siscpf)%r81d, & - rio_rxfmortrate_usto_siscpf => this%rvars(ir_rxfmortrate_usto_siscpf)%r81d, & + rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & + rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & + rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & + rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & - rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & - rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & - rio_rxfmortrate_crown_siscpf => this%rvars(ir_rxfmortrate_crown_siscpf)%r81d, & - rio_rxfmortrate_cambi_siscpf => this%rvars(ir_rxfmortrate_cambi_siscpf)%r81d, & + rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & + rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & + rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & + rio_rx_fmortrate_cambi_siscpf => this%rvars(ir_rx_fmortrate_cambi_siscpf)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & rio_growflx_fusion_siscpf => this%rvars(ir_growflx_fusion_siscpf)%r81d, & @@ -2286,22 +2286,22 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termcarea_usto_si => this%rvars(ir_termcarea_usto_si)%r81d, & rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & - rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & - rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & - rio_rxfmortcarea_cano_si => this%rvars(ir_rxfmortcarea_cano_si)%r81d, & - rio_rxfmortcarea_usto_si => this%rvars(ir_rxfmortcarea_usto_si)%r81d, & + rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & + rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & + rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & + rio_rx_fmortcarea_usto_si => this%rvars(ir_rx_fmortcarea_usto_si)%r81d, & rio_termcflux_cano_sipft => this%rvars(ir_termcflux_cano_sipft)%r81d, & rio_termcflux_usto_sipft => this%rvars(ir_termcflux_usto_sipft)%r81d, & rio_democflux_si => this%rvars(ir_democflux_si)%r81d, & rio_promcflux_si => this%rvars(ir_promcflux_si)%r81d, & rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & - rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & - rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & - rio_rxfmortcflux_cano_sipft => this%rvars(ir_rxfmortcflux_cano_sipft)%r81d, & - rio_rxfmortcflux_usto_sipft => this%rvars(ir_rxfmortcflux_usto_sipft)%r81d, & + rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & + rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & + rio_rx_fmortcflux_cano_sipft => this%rvars(ir_rx_fmortcflux_cano_sipft)%r81d, & + rio_rx_fmortcflux_usto_sipft => this%rvars(ir_rx_fmortcflux_usto_sipft)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & - rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & - rio_abg_rxfmort_flux_siscpf => this%rvars(ir_abg_rxfmort_flux_siscpf)%r81d, & + rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & + rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_landuse_config_si => this%rvars(ir_landuse_config_si)%int1d, & @@ -2312,14 +2312,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termnindiv_cano_sicdpf => this%rvars(ir_termnindiv_cano_sicdpf)%r81d, & rio_termcflux_usto_sicdsc => this%rvars(ir_termcflux_usto_sicdsc)%r81d, & rio_termnindiv_usto_sicdpf => this%rvars(ir_termnindiv_usto_sicdpf)%r81d, & - rio_fmortrate_cano_sicdpf => this%rvars(ir_fmortrate_cano_sicdpf)%r81d, & - rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & - rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & - rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & - rio_rxfmortrate_cano_sicdpf => this%rvars(ir_rxfmortrate_cano_sicdpf)%r81d, & - rio_rxfmortrate_usto_sicdpf => this%rvars(ir_rxfmortrate_usto_sicdpf)%r81d, & - rio_rxfmortcflux_cano_sicdsc => this%rvars(ir_rxfmortcflux_cano_sicdsc)%r81d, & - rio_rxfmortcflux_usto_sicdsc => this%rvars(ir_rxfmortcflux_usto_sicdsc)%r81d, & + rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & + rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & + rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & + rio_nonrx_fmortcflux_usto_sicdsc => this%rvars(ir_nonrx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortrate_cano_sicdpf => this%rvars(ir_rx_fmortrate_cano_sicdpf)%r81d, & + rio_rx_fmortrate_usto_sicdpf => this%rvars(ir_rx_fmortrate_usto_sicdpf)%r81d, & + rio_rx_fmortcflux_cano_sicdsc => this%rvars(ir_rx_fmortcflux_cano_sicdsc)%r81d, & + rio_rx_fmortcflux_usto_sicdsc => this%rvars(ir_rx_fmortcflux_usto_sicdsc)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d) @@ -2393,20 +2393,20 @@ subroutine set_restart_vectors(this,nc,nsites,sites) do i_scls = 1, nlevsclass do i_pft = 1, numpft - rio_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_canopy(i_scls, i_pft) - rio_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_ustory(i_scls, i_pft) + rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) + rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) rio_imortrate_siscpf(io_idx_si_scpf) = sites(s)%imort_rate(i_scls, i_pft) - rio_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_crown(i_scls, i_pft) - rio_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_cambial(i_scls, i_pft) - rio_rxfmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_canopy(i_scls, i_pft) - rio_rxfmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_ustory(i_scls, i_pft) - rio_rxfmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_crown(i_scls, i_pft) - rio_rxfmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_rate_cambial(i_scls, i_pft) + rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) + rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) + rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) + rio_rx_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_ustory(i_scls, i_pft) + rio_rx_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_crown(i_scls, i_pft) + rio_rx_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_cambial(i_scls, i_pft) rio_growflx_fusion_siscpf(io_idx_si_scpf) = sites(s)%growthflux_fusion(i_scls, i_pft) rio_abg_term_flux_siscpf(io_idx_si_scpf) = sites(s)%term_abg_flux(i_scls, i_pft) rio_abg_imort_flux_siscpf(io_idx_si_scpf) = sites(s)%imort_abg_flux(i_scls, i_pft) - rio_abg_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%fmort_abg_flux(i_scls, i_pft) - rio_abg_rxfmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rxfmort_abg_flux(i_scls, i_pft) + rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_abg_flux(i_scls, i_pft) + rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_abg_flux(i_scls, i_pft) io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) @@ -2422,10 +2422,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termcflux_usto_sipft(io_idx_si_pft_term) = sites(s)%term_carbonflux_ustory(i_term_type,i_pft) io_idx_si_pft_term = io_idx_si_pft_term + 1 end do - rio_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_canopy(i_pft) - rio_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_ustory(i_pft) - rio_rxfmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rxfmort_carbonflux_canopy(i_pft) - rio_rxfmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%rxfmort_carbonflux_ustory(i_pft) + rio_nonrx_cflux_cano_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) + rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) + rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_canopy(i_pft) + rio_rx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_ustory(i_pft) rio_imortcflux_sipft(io_idx_si_pft) = sites(s)%imort_carbonflux(i_pft) rio_dd_status_sift(io_idx_si_pft) = sites(s)%dstatus(i_pft) rio_dleafondate_sift(io_idx_si_pft) = sites(s)%dleafondate(i_pft) @@ -2816,14 +2816,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcflux_sicdsc(io_idx_si_cdsc) = sites(s)%imort_cflux_damage(i_cdam, i_scls) rio_termcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%term_cflux_canopy_damage(i_cdam, i_scls) rio_termcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%term_cflux_ustory_damage(i_cdam, i_scls) - rio_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) - rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) - rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) - rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) - rio_rxfmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%rxfmort_rate_canopy_damage(i_cdam, i_scls, i_pft) - rio_rxfmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%rxfmort_rate_ustory_damage(i_cdam, i_scls, i_pft) - rio_rxfmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%rxfmort_cflux_canopy_damage(i_cdam, i_scls) - rio_rxfmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%rxfmort_cflux_ustory_damage(i_cdam, i_scls) + rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) + rio_nonrx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%nonrx_fmort_cflux_ustory_damage(i_cdam, i_scls) + rio_rx_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%rx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_rx_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%rx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_rx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%rx_fmort_cflux_canopy_damage(i_cdam, i_scls) + rio_rx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%rx_fmort_cflux_ustory_damage(i_cdam, i_scls) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -2840,10 +2840,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_promcflux_si(io_idx_si) = sites(s)%promotion_carbonflux rio_imortcarea_si(io_idx_si) = sites(s)%imort_crownarea - rio_fmortcarea_cano_si(io_idx_si) = sites(s)%fmort_crownarea_canopy - rio_fmortcarea_usto_si(io_idx_si) = sites(s)%fmort_crownarea_ustory - rio_rxfmortcarea_cano_si(io_idx_si) = sites(s)%rxfmort_crownarea_canopy - rio_rxfmortcarea_usto_si(io_idx_si) = sites(s)%rxfmort_crownarea_ustory + rio_nonrx_fmortcarea_cano_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_canopy + rio_nonrx_fmortcarea_usto_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_ustory + rio_rx_fmortcarea_cano_si(io_idx_si) = sites(s)%rx_fmort_crownarea_canopy + rio_rx_fmortcarea_usto_si(io_idx_si) = sites(s)%rx_fmort_crownarea_ustory rio_cd_status_si(io_idx_si) = sites(s)%cstatus rio_nchill_days_si(io_idx_si) = sites(s)%nchilldays @@ -3302,15 +3302,15 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_area_pft_sift => this%rvars(ir_area_pft_sift)%r81d,& rio_seed_in_sift => this%rvars(ir_seed_in_sift)%r81d, & rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & - rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & - rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & - rio_rxfmortrate_cano_siscpf => this%rvars(ir_rxfmortrate_cano_siscpf)%r81d, & - rio_rxfmortrate_usto_siscpf => this%rvars(ir_rxfmortrate_usto_siscpf)%r81d, & + rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & + rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & + rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & + rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & - rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & - rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & - rio_rxfmortrate_crown_siscpf => this%rvars(ir_rxfmortrate_crown_siscpf)%r81d, & - rio_rxfmortrate_cambi_siscpf => this%rvars(ir_rxfmortrate_cambi_siscpf)%r81d, & + rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & + rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & + rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & + rio_rx_fmortrate_cambi_siscpf => this%rvars(ir_rx_fmortrate_cambi_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & @@ -3324,36 +3324,36 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_termcarea_cano_si => this%rvars(ir_termcarea_cano_si)%r81d, & rio_termcarea_usto_si => this%rvars(ir_termcarea_usto_si)%r81d, & rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & - rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & - rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & - rio_rxfmortcarea_cano_si => this%rvars(ir_rxfmortcarea_cano_si)%r81d, & - rio_rxfmortcarea_usto_si => this%rvars(ir_rxfmortcarea_usto_si)%r81d, & + rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & + rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & + rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & + rio_rx_fmortcarea_usto_si => this%rvars(ir_rx_fmortcarea_usto_si)%r81d, & rio_imortrate_sicdpf => this%rvars(ir_imortrate_sicdpf)%r81d, & rio_termnindiv_cano_sicdpf => this%rvars(ir_termnindiv_cano_sicdpf)%r81d, & rio_termnindiv_usto_sicdpf => this%rvars(ir_termnindiv_usto_sicdpf)%r81d, & rio_imortcflux_sicdsc => this%rvars(ir_imortcflux_sicdsc)%r81d, & rio_termcflux_cano_sicdsc => this%rvars(ir_termcflux_cano_sicdsc)%r81d, & rio_termcflux_usto_sicdsc => this%rvars(ir_termcflux_usto_sicdsc)%r81d, & - rio_fmortrate_cano_sicdpf => this%rvars(ir_fmortrate_cano_sicdpf)%r81d, & - rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & - rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & - rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & - rio_rxfmortrate_cano_sicdpf => this%rvars(ir_rxfmortrate_cano_sicdpf)%r81d, & - rio_rxfmortrate_usto_sicdpf => this%rvars(ir_rxfmortrate_usto_sicdpf)%r81d, & - rio_rxfmortcflux_cano_sicdsc => this%rvars(ir_rxfmortcflux_cano_sicdsc)%r81d, & - rio_rxfmortcflux_usto_sicdsc => this%rvars(ir_rxfmortcflux_usto_sicdsc)%r81d, & - rio_rxfmortcflux_cano_sipft => this%rvars(ir_rxfmortcflux_cano_sipft)%r81d, & - rio_rxfmortcflux_usto_sipft => this%rvars(ir_rxfmortcflux_usto_sipft)%r81d, & + rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & + rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & + rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & + rio_nonrx_fmortcflux_usto_sicdsc => this%rvars(ir_nonrx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortrate_cano_sicdpf => this%rvars(ir_rx_fmortrate_cano_sicdpf)%r81d, & + rio_rx_fmortrate_usto_sicdpf => this%rvars(ir_rx_fmortrate_usto_sicdpf)%r81d, & + rio_rx_fmortcflux_cano_sicdsc => this%rvars(ir_rx_fmortcflux_cano_sicdsc)%r81d, & + rio_rx_fmortcflux_usto_sicdsc => this%rvars(ir_rx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortcflux_cano_sipft => this%rvars(ir_rx_fmortcflux_cano_sipft)%r81d, & + rio_rx_fmortcflux_usto_sipft => this%rvars(ir_rx_fmortcflux_usto_sipft)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d, & rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & - rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & - rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & + rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & + rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & - rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & - rio_abg_rxfmort_flux_siscpf => this%rvars(ir_abg_rxfmort_flux_siscpf)%r81d ) + rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & + rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d ) totalcohorts = 0 @@ -3414,20 +3414,20 @@ subroutine get_restart_vectors(this, nc, nsites, sites) do i_scls = 1,nlevsclass do i_pft = 1, numpft - sites(s)%fmort_rate_canopy(i_scls, i_pft) = rio_fmortrate_cano_siscpf(io_idx_si_scpf) - sites(s)%fmort_rate_ustory(i_scls, i_pft) = rio_fmortrate_usto_siscpf(io_idx_si_scpf) - sites(s)%rxfmort_rate_canopy(i_scls, i_pft) = rio_rxfmortrate_cano_siscpf(io_idx_si_scpf) - sites(s)%rxfmort_rate_ustory(i_scls, i_pft) = rio_rxfmortrate_usto_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) = rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) = rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) = rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_ustory(i_scls, i_pft) = rio_rx_fmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%imort_rate(i_scls, i_pft) = rio_imortrate_siscpf(io_idx_si_scpf) - sites(s)%fmort_rate_crown(i_scls, i_pft) = rio_fmortrate_crown_siscpf(io_idx_si_scpf) - sites(s)%fmort_rate_cambial(i_scls, i_pft) = rio_fmortrate_cambi_siscpf(io_idx_si_scpf) - sites(s)%rxfmort_rate_crown(i_scls, i_pft) = rio_rxfmortrate_crown_siscpf(io_idx_si_scpf) - sites(s)%rxfmort_rate_cambial(i_scls, i_pft) = rio_rxfmortrate_cambi_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) = rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) = rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_crown(i_scls, i_pft) = rio_rx_fmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_cambial(i_scls, i_pft) = rio_rx_fmortrate_cambi_siscpf(io_idx_si_scpf) sites(s)%growthflux_fusion(i_scls, i_pft) = rio_growflx_fusion_siscpf(io_idx_si_scpf) sites(s)%term_abg_flux(i_scls,i_pft) = rio_abg_term_flux_siscpf(io_idx_si_scpf) sites(s)%imort_abg_flux(i_scls,i_pft) = rio_abg_imort_flux_siscpf(io_idx_si_scpf) - sites(s)%fmort_abg_flux(i_scls,i_pft) = rio_abg_fmort_flux_siscpf(io_idx_si_scpf) - sites(s)%rxfmort_abg_flux(i_scls,i_pft) = rio_abg_rxfmort_flux_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_abg_flux(i_scls,i_pft) = rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_abg_flux(i_scls,i_pft) = rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) = rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) @@ -3443,10 +3443,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%term_carbonflux_ustory(i_term_type,i_pft) = rio_termcflux_usto_sipft(io_idx_si_pft_term) io_idx_si_pft_term = io_idx_si_pft_term + 1 end do - sites(s)%fmort_carbonflux_canopy(i_pft) = rio_fmortcflux_cano_sipft(io_idx_si_pft) - sites(s)%fmort_carbonflux_ustory(i_pft) = rio_fmortcflux_usto_sipft(io_idx_si_pft) - sites(s)%rxfmort_carbonflux_canopy(i_pft) = rio_rxfmortcflux_cano_sipft(io_idx_si_pft) - sites(s)%rxfmort_carbonflux_ustory(i_pft) = rio_rxfmortcflux_usto_sipft(io_idx_si_pft) + sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) = rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) = rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) + sites(s)%rx_fmort_carbonflux_canopy(i_pft) = rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%rx_fmort_carbonflux_ustory(i_pft) = rio_rx_fmortcflux_usto_sipft(io_idx_si_pft) sites(s)%imort_carbonflux(i_pft) = rio_imortcflux_sipft(io_idx_si_pft) sites(s)%dstatus(i_pft) = rio_dd_status_sift(io_idx_si_pft) sites(s)%dleafondate(i_pft) = rio_dleafondate_sift(io_idx_si_pft) @@ -3875,14 +3875,14 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%imort_cflux_damage(i_cdam, i_scls) = rio_imortcflux_sicdsc(io_idx_si_cdsc) sites(s)%term_cflux_canopy_damage(i_cdam, i_scls) = rio_termcflux_cano_sicdsc(io_idx_si_cdsc) sites(s)%term_cflux_ustory_damage(i_cdam, i_scls) = rio_termcflux_usto_sicdsc(io_idx_si_cdsc) - sites(s)%fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_cano_sicdpf(io_idx_si_cdpf) - sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) - sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) - sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) - sites(s)%rxfmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_rxfmortrate_cano_sicdpf(io_idx_si_cdpf) - sites(s)%rxfmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_rxfmortrate_usto_sicdpf(io_idx_si_cdpf) - sites(s)%rxfmort_cflux_canopy_damage(i_cdam, i_scls) = rio_rxfmortcflux_cano_sicdsc(io_idx_si_cdsc) - sites(s)%rxfmort_cflux_ustory_damage(i_cdam, i_scls) = rio_rxfmortcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%nonrx_fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_nonrx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%rx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_rx_fmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%rx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_rx_fmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%rx_fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_rx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%rx_fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_rx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -3899,10 +3899,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%term_crownarea_canopy = rio_termcarea_cano_si(io_idx_si) sites(s)%term_crownarea_ustory = rio_termcarea_usto_si(io_idx_si) sites(s)%imort_crownarea = rio_imortcarea_si(io_idx_si) - sites(s)%fmort_crownarea_canopy = rio_fmortcarea_cano_si(io_idx_si) - sites(s)%fmort_crownarea_ustory = rio_fmortcarea_usto_si(io_idx_si) - sites(s)%rxfmort_crownarea_canopy = rio_rxfmortcarea_cano_si(io_idx_si) - sites(s)%rxfmort_crownarea_ustory = rio_rxfmortcarea_usto_si(io_idx_si) + sites(s)%nonrx_fmort_crownarea_canopy = rio_nonrx_fmortcarea_cano_si(io_idx_si) + sites(s)%nonrx_fmort_crownarea_ustory = rio_nonrx_fmortcarea_usto_si(io_idx_si) + sites(s)%rx_fmort_crownarea_canopy = rio_rx_fmortcarea_cano_si(io_idx_si) + sites(s)%rx_fmort_crownarea_ustory = rio_rx_fmortcarea_usto_si(io_idx_si) sites(s)%demotion_carbonflux = rio_democflux_si(io_idx_si) sites(s)%promotion_carbonflux = rio_promcflux_si(io_idx_si) From e16f15952d590a4472fed021146a7125cbed27b1 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 09:26:52 -0700 Subject: [PATCH 030/194] output wildfire and rxfire variables separately with variables for sum of rx and non-rx included --- main/FatesHistoryInterfaceMod.F90 | 287 ++++++++++++++++++------------ 1 file changed, 170 insertions(+), 117 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index f1423d2f91..5b952db7fc 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -449,23 +449,26 @@ module FatesHistoryInterfaceMod integer :: ih_fire_nignitions_si integer :: ih_fire_fdi_si integer :: ih_fire_intensity_area_product_si + integer :: ih_nonrx_intensity_area_product_si + integer :: ih_rx_intensity_area_product_si integer :: ih_spitfire_ros_si integer :: ih_effect_wspeed_si integer :: ih_tfc_ros_si integer :: ih_fire_intensity_si + integer :: ih_nonrx_intensity_si integer :: ih_fire_area_si + integer :: ih_nonrx_area_si integer :: ih_fire_fuel_bulkd_si integer :: ih_fire_fuel_eff_moist_si integer :: ih_fire_fuel_sav_si integer :: ih_fire_fuel_mef_si integer :: ih_sum_fuel_si integer :: ih_rx_burn_window_si - integer :: ih_rxfire_intensity_area_product_si - integer :: ih_rxfire_intensity_si - integer :: ih_rxfire_area_si - integer :: ih_rxfire_area_fuel_si - integer :: ih_rxfire_area_fi_si - integer :: ih_rxfire_area_final_si + integer :: ih_rx_intensity_si + integer :: ih_rx_area_si + integer :: ih_rx_area_fuel_si + integer :: ih_rx_area_fi_si + integer :: ih_rx_area_final_si integer :: ih_fragmentation_scaler_sl integer :: ih_nplant_si_scpf @@ -512,10 +515,10 @@ module FatesHistoryInterfaceMod integer :: ih_m11_si_scpf integer :: ih_m12_si_scpf - integer :: ih_crownfiremort_si_scpf - integer :: ih_cambialfiremort_si_scpf - integer :: ih_rxcrownfiremort_si_scpf - integer :: ih_rxcambialfiremort_si_scpf + integer :: ih_nonrx_crown_mort_si_scpf + integer :: ih_nonrx_cambial_mort_si_scpf + integer :: ih_rx_crown_mort_si_scpf + integer :: ih_rx_cambial_mort_si_scpf integer :: ih_abg_mortality_cflux_si_scpf integer :: ih_abg_productivity_cflux_si_scpf @@ -666,10 +669,12 @@ module FatesHistoryInterfaceMod integer :: ih_primarylands_area_si_age integer :: ih_area_burnt_si_age integer :: ih_rx_area_burnt_si_age + integer :: ih_nonrx_area_burnt_si_age ! integer :: ih_fire_rate_of_spread_front_si_age integer :: ih_fire_intensity_si_age integer :: ih_fire_sum_fuel_si_age - integer :: ih_rxfire_intensity_si_age + integer :: ih_rx_intensity_si_age + integer :: ih_nonrx_intensity_si_age ! indices to (site x height) variables integer :: ih_canopy_height_dist_si_height @@ -2422,12 +2427,15 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_fire_fuel_sav_si => this%hvars(ih_fire_fuel_sav_si)%r81d, & hio_fire_fuel_mef_si => this%hvars(ih_fire_fuel_mef_si)%r81d, & hio_sum_fuel_si => this%hvars(ih_sum_fuel_si)%r81d, & - hio_rxfire_intensity_si => this%hvars(ih_rxfire_intensity_si)%r81d, & - hio_rxfire_intensity_area_product_si => this%hvars(ih_rxfire_intensity_area_product_si)%r81d, & - hio_rxfire_area_si => this%hvars(ih_rxfire_area_si)%r81d, & - hio_rxfire_area_fuel_si => this%hvars(ih_rxfire_area_fuel_si)%r81d, & - hio_rxfire_area_fi_si => this%hvars(ih_rxfire_area_fi_si)%r81d, & - hio_rxfire_area_final_si => this%hvars(ih_rxfire_area_final_si)%r81d, & + hio_nonrx_intensity_si => this%hvars(ih_nonrx_intensity_si)%r81d, & + hio_nonrx_intensity_area_product_si => this%hvars(ih_nonrx_intensity_area_product_si)%r81d, & + hio_nonrx_area_si => this%hvars(ih_nonrx_area_si)%r81d, & + hio_rx_intensity_si => this%hvars(ih_rx_intensity_si)%r81d, & + hio_rx_intensity_area_product_si => this%hvars(ih_rx_intensity_area_product_si)%r81d, & + hio_rx_area_si => this%hvars(ih_rx_area_si)%r81d, & + hio_rx_area_fuel_si => this%hvars(ih_rx_area_fuel_si)%r81d, & + hio_rx_area_fi_si => this%hvars(ih_rx_area_fi_si)%r81d, & + hio_rx_area_final_si => this%hvars(ih_rx_area_final_si)%r81d, & hio_litter_in_si => this%hvars(ih_litter_in_si)%r81d, & hio_litter_out_si => this%hvars(ih_litter_out_si)%r81d, & hio_npp_si => this%hvars(ih_npp_si)%r81d, & @@ -2559,13 +2567,13 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_fire_fdi_si(io_si) = sites(s)%FDI ! total rx burnable fraction when fuel condition met - hio_rxfire_area_fuel_si(io_si) = sites(s)%rxfire_area_fuel * AREA_INV + hio_rx_area_fuel_si(io_si) = sites(s)%rx_area_fuel * AREA_INV - ! total rx burnable fraction when fuel and FI conditions met - hio_rxfire_area_fi_si(io_si) = sites(s)%rxfire_area_fi * AREA_INV + ! total rx burnable fraction when fuel and FI conditions met + hio_rx_area_fi_si(io_si) = sites(s)%rx_area_fi * AREA_INV - ! total rx burnable fraction when all conditions met - hio_rxfire_area_final_si(io_si) = sites(s)%rxfire_area_final * AREA_INV + ! total rx burnable fraction when all conditions met + hio_rx_area_final_si(io_si) = sites(s)%rx_area_final * AREA_INV ! If hydraulics are turned on, track the error terms associated with ! dynamics [kg/m2] @@ -2629,12 +2637,10 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) ! add site level mortality counting to crownarea diagnostic hio_canopy_mortality_crownarea_si(io_si) = hio_canopy_mortality_crownarea_si(io_si) + & sites(s)%fmort_crownarea_canopy + & - sites(s)%rxfmort_crownarea_canopy + & !add rx fire effect sites(s)%term_crownarea_canopy * days_per_year hio_ustory_mortality_crownarea_si(io_si) = hio_ustory_mortality_crownarea_si(io_si) + & sites(s)%fmort_crownarea_ustory + & - sites(s)%rxfmort_crownarea_ustory + & ! add rx fire effect sites(s)%term_crownarea_ustory * days_per_year + & sites(s)%imort_crownarea @@ -2690,19 +2696,21 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_tfc_ros_si(io_si) = hio_tfc_ros_si(io_si) + cpatch%TFC_ROS * cpatch%area * AREA_INV hio_fire_intensity_si(io_si) = hio_fire_intensity_si(io_si) + cpatch%FI * cpatch%area * AREA_INV * J_per_kJ hio_fire_area_si(io_si) = hio_fire_area_si(io_si) + cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day - hio_rxfire_intensity_si(io_si) = hio_rxfire_intensity_si(io_si) + cpatch%rxfire_FI * cpatch%area * AREA_INV * J_per_kJ - hio_rxfire_area_si(io_si) = hio_rxfire_area_si(io_si) + cpatch%rxfire_frac_burnt * cpatch%area * AREA_INV / sec_per_day + hio_nonrx_intensity_si(io_si) = hio_nonrx_intensity_si(io_si) + cpatch%nonrx_FI * cpatch%area * AREA_INV * J_per_kJ + hio_nonrx_area_si(io_si) = hio_nonrx_area_si(io_si) + cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV / sec_per_day + hio_rx_intensity_si(io_si) = hio_rx_intensity_si(io_si) + cpatch%rx_FI * cpatch%area * AREA_INV * J_per_kJ + hio_rx_area_si(io_si) = hio_rx_area_si(io_si) + cpatch%rx_frac_burnt * cpatch%area * AREA_INV / sec_per_day hio_fire_fuel_bulkd_si(io_si) = hio_fire_fuel_bulkd_si(io_si) + cpatch%fuel%bulk_density_notrunks * cpatch%area * AREA_INV hio_fire_fuel_eff_moist_si(io_si) = hio_fire_fuel_eff_moist_si(io_si) + cpatch%fuel%average_moisture_notrunks * cpatch%area * AREA_INV hio_fire_fuel_sav_si(io_si) = hio_fire_fuel_sav_si(io_si) + cpatch%fuel%SAV_notrunks * cpatch%area * AREA_INV / m_per_cm hio_fire_fuel_mef_si(io_si) = hio_fire_fuel_mef_si(io_si) + cpatch%fuel%MEF_notrunks * cpatch%area * AREA_INV hio_sum_fuel_si(io_si) = hio_sum_fuel_si(io_si) + cpatch%fuel%non_trunk_loading * cpatch%area * AREA_INV - hio_fire_intensity_area_product_si(io_si) = hio_fire_intensity_area_product_si(io_si) + & - cpatch%FI * cpatch%frac_burnt * cpatch%area * AREA_INV * J_per_kJ + hio_nonrx_intensity_area_product_si(io_si) = hio_nonrx_intensity_area_product_si(io_si) + & + cpatch%nonrx_FI * cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ - hio_rxfire_intensity_area_product_si(io_si) = hio_rxfire_intensity_area_product_si(io_si) + & - cpatch%rxfire_FI * cpatch%rxfire_frac_burnt * cpatch%area * AREA_INV * J_per_kJ + hio_rx_intensity_area_product_si(io_si) = hio_rx_intensity_area_product_si(io_si) + & + cpatch%rx_FI * cpatch%rx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ litt => cpatch%litter(element_pos(carbon12_element)) @@ -3192,10 +3200,10 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_m10_si_scpf => this%hvars(ih_m10_si_scpf)%r82d, & hio_m12_si_scpf => this%hvars(ih_m12_si_scpf)%r82d, & hio_m10_si_capf => this%hvars(ih_m10_si_capf)%r82d, & - hio_crownfiremort_si_scpf => this%hvars(ih_crownfiremort_si_scpf)%r82d, & - hio_cambialfiremort_si_scpf => this%hvars(ih_cambialfiremort_si_scpf)%r82d, & - hio_rxcrownfiremort_si_scpf => this%hvars(ih_rxcrownfiremort_si_scpf)%r82d, & - hio_rxcambialfiremort_si_scpf => this%hvars(ih_rxcambialfiremort_si_scpf)%r82d, & + hio_nonrx_crown_mort_si_scpf => this%hvars(ih_nonrx_crown_mort_si_scpf)%r82d, & + hio_nonrx_cambial_mort_si_scpf => this%hvars(ih_nonrx_cambial_mort_si_scpf)%r82d, & + hio_rx_crown_mort_si_scpf => this%hvars(ih_rx_crown_mort_si_scpf)%r82d, & + hio_rx_cambial_mort_si_scpf => this%hvars(ih_rx_cambial_mort_si_scpf)%r82d, & hio_abg_mortality_cflux_si_scpf => this%hvars(ih_abg_mortality_cflux_si_scpf)%r82d, & hio_abg_productivity_cflux_si_scpf => this%hvars(ih_abg_productivity_cflux_si_scpf)%r82d, & hio_burn_flux_elem => this%hvars(ih_burn_flux_elem)%r82d, & @@ -3287,13 +3295,15 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_burnedarea_si_landuse => this%hvars(ih_burnedarea_si_landuse)%r82d, & hio_area_burnt_si_age => this%hvars(ih_area_burnt_si_age)%r82d, & hio_rx_area_burnt_si_age => this%hvars(ih_rx_area_burnt_si_age)%r82d, & - ! hio_fire_rate_of_spread_front_si_age => this%hvars(ih_fire_rate_of_spread_front_si_age)%r82d, & + hio_nonrx_area_burnt_si_age => this%hvars(ih_nonrx_area_burnt_si_age)%r82d, & + ! hio_fire_rate_of_spread_front_si_age => this%hvars(ih_fire_rate_of_spread_front_si_age)%r82d, & hio_fire_intensity_si_age => this%hvars(ih_fire_intensity_si_age)%r82d, & hio_fire_sum_fuel_si_age => this%hvars(ih_fire_sum_fuel_si_age)%r82d, & hio_burnt_frac_litter_si_fuel => this%hvars(ih_burnt_frac_litter_si_fuel)%r82d, & hio_fuel_amount_si_fuel => this%hvars(ih_fuel_amount_si_fuel)%r82d, & hio_fuel_amount_age_fuel => this%hvars(ih_fuel_amount_age_fuel)%r82d, & - hio_rxfire_intensity_si_age => this%hvars(ih_rxfire_intensity_si_age)%r82d, & + hio_rx_intensity_si_age => this%hvars(ih_rx_intensity_si_age)%r82d, & + hio_nonrx_intensity_si_age => this%hvars(ih_nonrx_intensity_si_age)%r82d, & hio_canopy_height_dist_si_height => this%hvars(ih_canopy_height_dist_si_height)%r82d, & hio_leaf_height_dist_si_height => this%hvars(ih_leaf_height_dist_si_height)%r82d, & hio_litter_moisture_si_fuel => this%hvars(ih_litter_moisture_si_fuel)%r82d, & @@ -3503,7 +3513,10 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day hio_rx_area_burnt_si_age(io_si,cpatch%age_class) = hio_rx_area_burnt_si_age(io_si,cpatch%age_class) + & - cpatch%rxfire_frac_burnt * cpatch%area * AREA_INV / sec_per_day + cpatch%rx_frac_burnt * cpatch%area * AREA_INV / sec_per_day + + hio_nonrx_area_burnt_si_age(io_si,cpatch%age_class) = hio_nonrx_area_burnt_si_age(io_si,cpatch%age_class) + & + cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV / sec_per_day ! hio_fire_rate_of_spread_front_si_age(io_si, cpatch%age_class) = hio_fire_rate_of_spread_si_age(io_si, cpatch%age_class) + & ! cpatch%ros_front * cpatch*frac_burnt * cpatch%area * AREA_INV @@ -3512,8 +3525,11 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_fire_intensity_si_age(io_si, cpatch%age_class) = hio_fire_intensity_si_age(io_si, cpatch%age_class) + & cpatch%FI * cpatch%frac_burnt * cpatch%area * AREA_INV * J_per_kJ - hio_rxfire_intensity_si_age(io_si, cpatch%age_class) = hio_rxfire_intensity_si_age(io_si, cpatch%age_class) + & - cpatch%rxfire_FI * cpatch%rxfire_frac_burnt * cpatch%area * AREA_INV * J_per_kJ + hio_rx_intensity_si_age(io_si, cpatch%age_class) = hio_rx_intensity_si_age(io_si, cpatch%age_class) + & + cpatch%rx_FI * cpatch%rx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ + + hio_nonrx_intensity_si_age(io_si, cpatch%age_class) = hio_nonrx_intensity_si_age(io_si, cpatch%age_class) + & + cpatch%nonrx_FI * cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ ! Fuel sum [kg/m2] hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) = hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) + & @@ -4447,38 +4463,38 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & sites(s)%imort_rate(i_scls, ft) / m2_per_ha - ! fire mortality from the site-level diagnostic rates - hio_m5_si_scpf(io_si,i_scpf) = (sites(s)%fmort_rate_canopy(i_scls, ft) + & - sites(s)%fmort_rate_ustory(i_scls, ft)) / m2_per_ha - ! prescribed fire mortality + ! wildfire mortality from the site-level diagnostic rates + hio_m5_si_scpf(io_si,i_scpf) = (sites(s)%nonrx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%nonrx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha hio_m5_si_scls(io_si,i_scls) = hio_m5_si_scls(io_si,i_scls) + & - (sites(s)%fmort_rate_canopy(i_scls, ft) + & - sites(s)%fmort_rate_ustory(i_scls, ft)) / m2_per_ha - hio_m12_si_scpf(io_si,i_scpf) = (sites(s)%rxfmort_rate_canopy(i_scls,ft) + & - sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha + (sites(s)%nonrx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%nonrx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha + ! prescribed fire mortality + hio_m12_si_scpf(io_si,i_scpf) = (sites(s)%rx_fmort_rate_canopy(i_scls,ft) + & + sites(s)%rx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha hio_m12_si_scls(io_si,i_scls) = hio_m12_si_scls(io_si,i_scls) + & - (sites(s)%rxfmort_rate_canopy(i_scls, ft) + & - sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha - ! - hio_crownfiremort_si_scpf(io_si,i_scpf) = sites(s)%fmort_rate_crown(i_scls, ft) / m2_per_ha - hio_cambialfiremort_si_scpf(io_si,i_scpf) = sites(s)%fmort_rate_cambial(i_scls, ft) / m2_per_ha + (sites(s)%rx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%rx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha + ! wildfire crown and cambial mort + hio_nonrx_crown_mort_si_scpf(io_si,i_scpf) = sites(s)%nonrx_fmort_rate_crown(i_scls, ft) / m2_per_ha + hio_nonrx_cambial_mort_si_scpf(io_si,i_scpf) = sites(s)%nonrx_fmort_rate_cambial(i_scls, ft) / m2_per_ha ! prescribed fire crown and cambial mort - hio_rxcrownfiremort_si_scpf(io_si,i_scpf) = sites(s)%rxfmort_rate_crown(i_scls, ft) / m2_per_ha - hio_rxcambialfiremort_si_scpf(io_si,i_scpf) = sites(s)%rxfmort_rate_cambial(i_scls, ft) / m2_per_ha + hio_rx_crown_mort_si_scpf(io_si,i_scpf) = sites(s)%rx_fmort_rate_crown(i_scls, ft) / m2_per_ha + hio_rx_cambial_mort_si_scpf(io_si,i_scpf) = sites(s)%rx_fmort_rate_cambial(i_scls, ft) / m2_per_ha ! ! fire components of overall canopy and understory mortality hio_mortality_canopy_si_scpf(io_si,i_scpf) = hio_mortality_canopy_si_scpf(io_si,i_scpf) + & - (sites(s)%fmort_rate_canopy(i_scls, ft) + sites(s)%rxfmort_rate_canopy(i_scls, ft)) / m2_per_ha + sites(s)%fmort_rate_canopy(i_scls, ft) / m2_per_ha hio_mortality_canopy_si_scls(io_si,i_scls) = hio_mortality_canopy_si_scls(io_si,i_scls) + & - (sites(s)%fmort_rate_canopy(i_scls, ft) + sites(s)%rxfmort_rate_canopy(i_scls, ft)) / m2_per_ha + sites(s)%fmort_rate_canopy(i_scls, ft) / m2_per_ha ! the fire mortality rates for each layer are total dead, since the usable ! output will then normalize by the counts, we are allowed to sum over layers hio_mortality_understory_si_scpf(io_si,i_scpf) = hio_mortality_understory_si_scpf(io_si,i_scpf) + & - (sites(s)%fmort_rate_ustory(i_scls, ft) + sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha + sites(s)%fmort_rate_ustory(i_scls, ft) / m2_per_ha hio_mortality_understory_si_scls(io_si,i_scls) = hio_mortality_understory_si_scls(io_si,i_scls) + & - (sites(s)%fmort_rate_ustory(i_scls, ft) + sites(s)%rxfmort_rate_ustory(i_scls, ft)) / m2_per_ha + sites(s)%fmort_rate_ustory(i_scls, ft) / m2_per_ha ! ! for scag variables, also treat as happening in the newly-disurbed patch @@ -4500,15 +4516,12 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) do ft = 1, numpft hio_mortality_carbonflux_si_pft(io_si,ft) = hio_mortality_carbonflux_si_pft(io_si,ft) + & (sites(s)%fmort_carbonflux_canopy(ft) + & - sites(s)%fmort_carbonflux_ustory(ft) + & - sites(s)%rxfmort_carbonflux_canopy(ft) + & - sites(s)%rxfmort_carbonflux_ustory(ft)) / g_per_kg + & + sites(s)%fmort_carbonflux_ustory(ft)) / g_per_kg + & sites(s)%imort_carbonflux(ft) + & sum(sites(s)%term_carbonflux_ustory(:,ft)) * days_per_sec * ha_per_m2 + & sum(sites(s)%term_carbonflux_canopy(:,ft)) * days_per_sec * ha_per_m2 - hio_firemortality_carbonflux_si_pft(io_si,ft) = (sites(s)%fmort_carbonflux_canopy(ft) + & - sites(s)%rxfmort_carbonflux_canopy(ft)) / g_per_kg + hio_firemortality_carbonflux_si_pft(io_si,ft) = sites(s)%fmort_carbonflux_canopy(ft) / g_per_kg end do ! add imort and fmort to aboveground woody mortality @@ -4516,7 +4529,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) do i_scls = 1,nlevsclass i_scpf = (ft-1)*nlevsclass + i_scls hio_abg_mortality_cflux_si_scpf(io_si,i_scpf) = hio_abg_mortality_cflux_si_scpf(io_si,i_scpf) + & - ((sites(s)%fmort_abg_flux(i_scls,ft) + sites(s)%rxfmort_abg_flux(i_scls,ft)) / g_per_kg ) + & + (sites(s)%fmort_abg_flux(i_scls,ft) / g_per_kg ) + & sites(s)%imort_abg_flux(i_scls,ft) + & (sites(s)%term_abg_flux(i_scls,ft) * days_per_sec * ha_per_m2 ) end do @@ -4539,22 +4552,18 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) (sites(s)%term_nindivs_ustory_damage(icdam, i_scls, ft) * days_per_year) + & sites(s)%imort_rate_damage(icdam, i_scls, ft) + & sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft) + & - sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft) + & - sites(s)%rxfmort_rate_canopy_damage(icdam, i_scls, ft) + & - sites(s)%rxfmort_rate_ustory_damage(icdam, i_scls, ft) ) / m2_per_ha + sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft)) / m2_per_ha this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf) + & ( sites(s)%term_nindivs_canopy_damage(icdam,i_scls,ft) * days_per_year + & - sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft) + & - sites(s)%rxfmort_rate_canopy_damage(icdam, i_scls, ft) )/ m2_per_ha + sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft))/ m2_per_ha this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) + & ( sites(s)%term_nindivs_ustory_damage(icdam, i_scls,ft) * days_per_year + & sites(s)%imort_rate_damage(icdam, i_scls, ft) + & - sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft) + & - sites(s)%rxfmort_rate_ustory_damage(icdam, i_scls, ft))/ m2_per_ha + sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft))/ m2_per_ha end do end do @@ -4572,13 +4581,20 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) sites(s)%fmort_rate_crown(:,:) = 0._r8 sites(s)%growthflux_fusion(:,:) = 0._r8 sites(s)%fmort_abg_flux(:,:) = 0._r8 - sites(s)%rxfmort_rate_canopy(:,:) = 0._r8 - sites(s)%rxfmort_rate_ustory(:,:) = 0._r8 - sites(s)%rxfmort_carbonflux_canopy(:) = 0._r8 - sites(s)%rxfmort_carbonflux_ustory(:) = 0._r8 - sites(s)%rxfmort_rate_cambial(:,:) = 0._r8 - sites(s)%rxfmort_rate_crown(:,:) = 0._r8 - sites(s)%rxfmort_abg_flux(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_canopy(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_ustory(:,:) = 0._r8 + sites(s)%nonrx_fmort_carbonflux_canopy(:) = 0._r8 + sites(s)%nonrx_fmort_carbonflux_ustory(:) = 0._r8 + sites(s)%nonrx_fmort_rate_cambial(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_crown(:,:) = 0._r8 + sites(s)%nonrx_fmort_abg_flux(:,:) = 0._r8 + sites(s)%rx_fmort_rate_canopy(:,:) = 0._r8 + sites(s)%rx_fmort_rate_ustory(:,:) = 0._r8 + sites(s)%rx_fmort_carbonflux_canopy(:) = 0._r8 + sites(s)%rx_fmort_carbonflux_ustory(:) = 0._r8 + sites(s)%rx_fmort_rate_cambial(:,:) = 0._r8 + sites(s)%rx_fmort_rate_crown(:,:) = 0._r8 + sites(s)%rx_fmort_abg_flux(:,:) = 0._r8 sites(s)%imort_abg_flux(:,:) = 0._r8 sites(s)%term_abg_flux(:,:) = 0._r8 @@ -4592,10 +4608,14 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) sites(s)%fmort_rate_ustory_damage(:,:,:) = 0._r8 sites(s)%fmort_cflux_canopy_damage(:,:) = 0._r8 sites(s)%fmort_cflux_ustory_damage(:,:) = 0._r8 - sites(s)%rxfmort_rate_canopy_damage(:,:,:) = 0._r8 - sites(s)%rxfmort_rate_ustory_damage(:,:,:) = 0._r8 - sites(s)%rxfmort_cflux_canopy_damage(:,:) = 0._r8 - sites(s)%rxfmort_cflux_ustory_damage(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + sites(s)%nonrx_fmort_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%nonrx_fmort_cflux_ustory_damage(:,:) = 0._r8 + sites(s)%rx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + sites(s)%rx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + sites(s)%rx_fmort_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%rx_fmort_cflux_ustory_damage(:,:) = 0._r8 ! pass the recruitment rate as a flux to the history, and then reset the recruitment buffer do ft = 1, numpft @@ -6224,10 +6244,10 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_nesterov_fire_danger_si) - call this%set_history_var(vname='FATES_RX_BURN_WINDOW', units='', & - long='prescribed fire burn window', use_default='active', & - avgflag='A', vtype=site_r8, hlms='CLM:ALM', & - upfreq=1, ivar=ivar, initialize=initialize_variables, & + call this%set_history_var(vname='FATES_RX_BURN_WINDOW', units='', & + long='fraction of time when prescribed fire burn window presents', & + use_default='active',avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & index=ih_rx_burn_window_si) call this%set_history_var(vname='FATES_IGNITIONS', & @@ -6263,61 +6283,81 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_FIRE_INTENSITY', & units='J m-1 s-1', & - long='spitfire surface fireline intensity in J per m per second', & + long='spitfire surface fireline intensity in J per m per second, sum of rx and wildfire', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_fire_intensity_si) call this%set_history_var(vname='FATES_FIRE_INTENSITY_BURNFRAC', & units='J m-1 s-1', & - long='product of surface fire intensity and burned area fraction -- divide by FATES_BURNFRAC to get area-weighted mean intensity', & + long='product of surface fire intensity and burned area fraction, sum of rx and wildfire-- divide by FATES_BURNFRAC to get area-weighted mean intensity', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_fire_intensity_area_product_si) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY', & + units='J m-1 s-1', & + long='spitfire surface fireline intensity of wildfire in J per m per second', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_nonrx_intensity_si) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY_BURNFRAC', & + units='J m-1 s-1', & + long='product of wildfire intensity and burned fraction -- divide by FATES_WILDFIRE_BURNFRAC to get area-weighted mean intensity', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_nonrx_intensity_area_product_si) call this%set_history_var(vname='FATES_RXFIRE_INTENSITY', & units='J m-1 s-1', & - long='spitfire surface fireline intensity of prescried fire in J per m per second', & + long='spitfire surface fireline intensity of prescribed fire in J per m per second', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_intensity_si) + index=ih_rx_intensity_si) call this%set_history_var(vname='FATES_RXFIRE_INTENSITY_BURNFRAC', & units='J m-1 s-1', & long='product of prescribed fire intensity and burned fraction -- to be devided by FATES_RXFIRE_BURNFRAC to get area-weighted mean intensity', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_intensity_area_product_si) + index=ih_rx_intensity_area_product_si) call this%set_history_var(vname='FATES_BURNFRAC', units='s-1', & - long='burned area fraction per second', use_default='active', & + long='totaL burned area fraction per second -- sum of rxfire and wildfire burnt frac', use_default='active', & avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_fire_area_si) + call this%set_history_var(vname='FATES_WILDFIRE_BURNFRAC', units='s-1', & + long='burned area fraction per second by wildfire', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_nonrx_area_si) + call this%set_history_var(vname='FATES_RXFIRE_BURNFRAC', units='s-1', & long='burned area fraction per second by prescribed fire', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_area_si) + index=ih_rx_area_si) call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FUEL', units='', & long='burnable area fraction by Rx fire when fuel cond. met', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_area_fuel_si) + index=ih_rx_area_fuel_si) call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FI', units='', & long='burnable area fraction by Rx fire when fuel and FI cond. met', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_area_fi_si) + index=ih_rx_area_fi_si) call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FINAL', units='', & long='burnable area fraction by Rx fire when all cond. met', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index=ih_rxfire_area_final_si) + index=ih_rx_area_final_si) call this%set_history_var(vname='FATES_FUEL_MEF', units='m3 m-3', & long='fuel moisture of extinction (volumetric)', & @@ -7115,18 +7155,31 @@ subroutine define_history_vars(this, initialize_variables) index = ih_fuel_amount_age_fuel) call this%set_history_var(vname='FATES_BURNFRAC_AP', units='s-1', & - long='spitfire fraction area burnt (per second) by patch age', & + long='spitfire fraction area burnt (per second) by patch age, sum of rx and wildfire', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & - upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index = ih_area_burnt_si_age) call this%set_history_var(vname='FATES_FIRE_INTENSITY_BURNFRAC_AP', & units='J m-1 s-1', & - long='product of fire intensity and burned fraction, resolved by patch age (so divide by FATES_BURNFRAC_AP to get burned-area-weighted-average intensity)', & + long='product of fire intensity and burned fraction, sum of rx and wildfire, resolved by patch age (so divide by FATES_BURNFRAC_AP to get area-weighted mean intensity)', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & - upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index = ih_fire_intensity_si_age) + call this%set_history_var(vname='FATES_WILDFIRE_BURNFRAC_AP', units='s-1', & + long='spitfire fraction area burnt due to wildfire by patch age', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index = ih_nonrx_area_burnt_si_age) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY_BURNFRAC_AP', & + units='J m-1 s-1', & + long='product of wildfire intensity and burned fraction, resolved by patch age, divide by FATES_WILDFIRE_BURNFRAC_AP to get area-weighted mean intensity)', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index = ih_nonrx_intensity_si_age) + call this%set_history_var(vname='FATES_RXFIRE_BURNFRAC_AP', units='s-1', & long='spitfire fraction area burnt due to prescribed fire by patch age', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & @@ -7138,7 +7191,7 @@ subroutine define_history_vars(this, initialize_variables) long='product of prescribed fire intensity and burned fraction by patch age, to be devided by FATES_RXFIRE_BURNFRAC_AP to get area-weighted mean intensity)', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & upfreq=1, ivar=ivar, initialize=initialize_variables, & - index = ih_rxfire_intensity_si_age) + index = ih_rx_intensity_si_age) call this%set_history_var(vname='FATES_FUEL_AMOUNT_AP', units='kg m-2', & long='spitfire ground fuel (kg carbon per m2) related to FATES_ROS (omits 1000hr fuels) within each patch age bin (divide by FATES_PATCHAREA_AP to get fuel per unit area of that-age patch)', & @@ -7719,47 +7772,47 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_m4_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_FIRE_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_SZPF', & units = 'm-2 yr-1', & - long='fire mortality by pft/size in number of plants per m2 per year', & + long='wildfire mortality by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_m5_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_CROWNSCORCH_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_CROWN_SZPF', & units = 'm-2 yr-1', & - long='fire mortality from crown scorch by pft/size in number of plants per m2 per year', & + long='wildfire mortality from crown scorch by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_crownfiremort_si_scpf) + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index = ih_nonrx_crown_mort_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_CAMBIALBURN_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_CAMBIAL_SZPF', & units = 'm-2 yr-1', & - long='fire mortality from cambial burn by pft/size in number of plants per m2 per year', & + long='wildfire mortality from cambial burn by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_cambialfiremort_si_scpf) + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index = ih_nonrx_cambial_mort_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_RXFIRE_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_RXFIRE_SZPF', & units = 'm-2 yr-1', & long='prescribed fire mortality by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & hlms='CLM:ALM', upfreq=1, ivar=ivar, & initialize=initialize_variables, index = ih_m12_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_RXCROWN_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_RXCROWN_SZPF', & units = 'm-2 yr-1', & long='fire mortality from crown scorch due to prescribed fire by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & hlms='CLM:ALM', upfreq=1, ivar=ivar, & - initialize=initialize_variables, index = ih_rxcrownfiremort_si_scpf) + initialize=initialize_variables, index = ih_rx_crown_mort_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_RXCAMBIAL_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_RXCAMBIAL_SZPF', & units = 'm-2 yr-1', & long='fire mortality from cambial kill due to prescribed fire by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & hlms='CLM:ALM', upfreq=1, ivar=ivar, & - initialize=initialize_variables, index = ih_rxcambialfiremort_si_scpf) + initialize=initialize_variables, index = ih_rx_cambial_mort_si_scpf) call this%set_history_var(vname='FATES_MORTALITY_TERMINATION_SZPF', & units = 'm-2 yr-1', & From 86f17d31bf32eb03251cd3cde8516f3753530d71 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 09:29:05 -0700 Subject: [PATCH 031/194] correct prescribed fire typo in param file --- parameter_files/fates_params_default.cdl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index e53de33e35..202fe84144 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -911,25 +911,25 @@ variables: fates_rxfire_temp_lwthreshold:long_name= "minimum temprature threshold for conducting prescribed fire"; double fates_rxfire_rh_upthreshold ; fates_rxfire_rh_upthreshold:units = "%"; - fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold for conducting prescribeb fire"; + fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold for conducting prescribed fire"; double fates_rxfire_rh_lwthreshold ; fates_rxfire_rh_lwthreshold:units = "%"; - fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold for conducting prescribeb fire"; + fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold for conducting prescribed fire"; double fates_rxfire_wind_upthreshold ; fates_rxfire_wind_upthreshold:units = "m/s"; - fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold for conducting prescribeb fire"; + fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold for conducting prescribed fire"; double fates_rxfire_wind_lwthreshold ; fates_rxfire_wind_lwthreshold:units = "m/s"; - fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold for conducting prescribeb fire"; + fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold for conducting prescribed fire"; double fates_rxfire_AB ; fates_rxfire_AB:units = "fraction/day"; fates_rxfire_AB:long_name= "daily burn capacity of prescribed fire"; double fates_rxfire_min_threshold ; fates_rxfire_min_threshold:units = "kJ/m/s or kW/s"; - fates_rxfire_min_threshold:long_name= "minimum energy threshold for conducting prescribeb fire"; + fates_rxfire_min_threshold:long_name= "minimum energy threshold for conducting prescribed fire"; double fates_rxfire_max_threshold ; fates_rxfire_max_threshold:units = "kJ/m/s or kW/s"; - fates_rxfire_max_threshold:long_name= "maximum energy threshold for conducting prescribeb fire"; + fates_rxfire_max_threshold:long_name= "maximum energy threshold for conducting prescribed fire"; double fates_rxfire_fuel_min ; fates_rxfire_fuel_min:units = "kgC/m2"; fates_rxfire_fuel_min:long_name= "minimum fuel load at the patch level for prescribed fire to occur"; From d79e39cac502b8ca191a84d9755581ef05427c07 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 09:34:46 -0700 Subject: [PATCH 032/194] add in-code documentation for explaining how burn window is checked --- fire/SFFireWeatherMod.F90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 6639030c9a..3f9a05179c 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -91,6 +91,12 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up real(r8) :: ws_check !intermediate value derived from wind speed condition check if(.not. rxfire_switch) return + + ! check if ambient temperature, relative humidity, and wind speed + ! are within user defined ranges by comparing current weather + ! condition to the lower and upper bounds defined. when within range, + ! it should result in negative value or zero (at the boundary condition) + ! for each check below t_check = (temp_C - temp_low)*(temp_C - temp_up) rh_check = (rh - rh_low)*(rh - rh_up) From 9a87c8bf2ec18ba7cfa524ad7c79ff76da91c785 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 10:58:15 -0700 Subject: [PATCH 033/194] fix introduced bug during renaming --- main/FatesHistoryInterfaceMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 5b952db7fc..333c12d3ed 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -2567,13 +2567,13 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_fire_fdi_si(io_si) = sites(s)%FDI ! total rx burnable fraction when fuel condition met - hio_rx_area_fuel_si(io_si) = sites(s)%rx_area_fuel * AREA_INV + hio_rx_area_fuel_si(io_si) = sites(s)%rxfire_area_fuel * AREA_INV ! total rx burnable fraction when fuel and FI conditions met - hio_rx_area_fi_si(io_si) = sites(s)%rx_area_fi * AREA_INV + hio_rx_area_fi_si(io_si) = sites(s)%rxfire_area_fi * AREA_INV ! total rx burnable fraction when all conditions met - hio_rx_area_final_si(io_si) = sites(s)%rx_area_final * AREA_INV + hio_rx_area_final_si(io_si) = sites(s)%rxfire_area_final * AREA_INV ! If hydraulics are turned on, track the error terms associated with ! dynamics [kg/m2] From c8828ac6702ab51da67c88ff8e43da801c4e9656 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 11:06:12 -0700 Subject: [PATCH 034/194] fix typo --- biogeochem/EDCohortDynamicsMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 4cd6791c0d..983a307653 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1020,7 +1020,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%fire_mort = (currentCohort%n*currentCohort%fire_mort + & nextc%n*nextc%fire_mort)/newn - currentCohort%nonrx_mort = (currentCohort%n*currentCohort*nonrx_mort + & + currentCohort%nonrx_mort = (currentCohort%n*currentCohort%nonrx_mort + & nextc%n*nextc%nonrx_mort)/newn currentCohort%rx_mort = (currentCohort%n*currentCohort%rx_mort + & From dadc6793edd7ea3e376113cee6d01882692f3716 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 11:26:03 -0700 Subject: [PATCH 035/194] rename wildfire and rxfire vairables --- main/EDInitMod.F90 | 113 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 57 deletions(-) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 7be857ab2d..281e6b84ec 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -146,14 +146,14 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%demotion_rate(1:nlevsclass)) allocate(site_in%promotion_rate(1:nlevsclass)) allocate(site_in%imort_rate(1:nlevsclass,1:numpft)) - allocate(site_in%fmort_rate_canopy(1:nlevsclass,1:numpft)) - allocate(site_in%fmort_rate_ustory(1:nlevsclass,1:numpft)) - allocate(site_in%fmort_rate_cambial(1:nlevsclass,1:numpft)) - allocate(site_in%fmort_rate_crown(1:nlevsclass,1:numpft)) - allocate(site_in%rxfmort_rate_canopy(1:nlevsclass,1:numpft)) - allocate(site_in%rxfmort_rate_ustory(1:nlevsclass,1:numpft)) - allocate(site_in%rxfmort_rate_cambial(1:nlevsclass,1:numpft)) - allocate(site_in%rxfmort_rate_crown(nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_crown(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_crown(nlevsclass,1:numpft)) allocate(site_in%growthflux_fusion(1:nlevsclass,1:numpft)) allocate(site_in%mass_balance(1:num_elements)) allocate(site_in%iflux_balance(1:num_elements)) @@ -165,15 +165,14 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_cflux_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%term_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%term_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) - allocate(site_in%fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) - allocate(site_in%fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%rxfmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) - allocate(site_in%rxfmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) - allocate(site_in%rxfmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%rxfmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%nonrx_fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%nonrx_fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%nonrx_fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%nonrx_fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rx_fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rx_fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rx_fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rx_fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) else allocate(site_in%term_nindivs_canopy_damage(1,1,1)) allocate(site_in%term_nindivs_ustory_damage(1,1,1)) @@ -181,28 +180,28 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_cflux_damage(1,1)) allocate(site_in%term_cflux_canopy_damage(1,1)) allocate(site_in%term_cflux_ustory_damage(1,1)) - allocate(site_in%fmort_rate_canopy_damage(1,1,1)) - allocate(site_in%fmort_rate_ustory_damage(1,1,1)) - allocate(site_in%fmort_cflux_canopy_damage(1,1)) - allocate(site_in%fmort_cflux_ustory_damage(1,1)) - allocate(site_in%rxfmort_rate_canopy_damage(1,1,1)) - allocate(site_in%rxfmort_rate_ustory_damage(1,1,1)) - allocate(site_in%rxfmort_cflux_canopy_damage(1,1)) - allocate(site_in%rxfmort_cflux_ustory_damage(1,1)) + allocate(site_in%nonrx_fmort_rate_canopy_damage(1,1,1)) + allocate(site_in%nonrx_fmort_rate_ustory_damage(1,1,1)) + allocate(site_in%nonrx_fmort_cflux_canopy_damage(1,1)) + allocate(site_in%nonrx_fmort_cflux_ustory_damage(1,1)) + allocate(site_in%rx_fmort_rate_canopy_damage(1,1,1)) + allocate(site_in%rx_fmort_rate_ustory_damage(1,1,1)) + allocate(site_in%rx_fmort_cflux_canopy_damage(1,1)) + allocate(site_in%rx_fmort_cflux_ustory_damage(1,1)) end if allocate(site_in%term_carbonflux_canopy(1:n_term_mort_types,1:numpft)) allocate(site_in%term_carbonflux_ustory(1:n_term_mort_types,1:numpft)) allocate(site_in%imort_carbonflux(1:numpft)) - allocate(site_in%fmort_carbonflux_canopy(1:numpft)) - allocate(site_in%fmort_carbonflux_ustory(1:numpft)) - allocate(site_in%rxfmort_carbonflux_canopy(1:numpft)) - allocate(site_in%rxfmort_carbonflux_ustory(1:numpft)) + allocate(site_in%nonrx_fmort_carbonflux_canopy(1:numpft)) + allocate(site_in%nonrx_fmort_carbonflux_ustory(1:numpft)) + allocate(site_in%rx_fmort_carbonflux_canopy(1:numpft)) + allocate(site_in%rx_fmort_carbonflux_ustory(1:numpft)) allocate(site_in%term_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%imort_abg_flux(1:nlevsclass,1:numpft)) - allocate(site_in%fmort_abg_flux(1:nlevsclass,1:numpft)) - allocate(site_in%rxfmort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_abg_flux(1:nlevsclass,1:numpft)) site_in%nlevsoil = bc_in%nlevsoil @@ -330,31 +329,31 @@ subroutine zero_site( site_in ) site_in%term_crownarea_canopy = 0._r8 site_in%term_crownarea_ustory = 0._r8 site_in%imort_crownarea = 0._r8 - site_in%fmort_crownarea_canopy = 0._r8 - site_in%fmort_crownarea_ustory = 0._r8 - site_in%rxfmort_crownarea_canopy = 0._r8 - site_in%rxfmort_crownarea_ustory = 0._r8 + site_in%nonrx_fmort_crownarea_canopy = 0._r8 + site_in%nonrx_fmort_crownarea_ustory = 0._r8 + site_in%rx_fmort_crownarea_canopy = 0._r8 + site_in%rx_fmort_crownarea_ustory = 0._r8 site_in%term_carbonflux_canopy(:,:) = 0._r8 site_in%term_carbonflux_ustory(:,:) = 0._r8 site_in%recruitment_rate(:) = 0._r8 site_in%imort_rate(:,:) = 0._r8 site_in%imort_carbonflux(:) = 0._r8 - site_in%fmort_rate_canopy(:,:) = 0._r8 - site_in%fmort_rate_ustory(:,:) = 0._r8 - site_in%fmort_carbonflux_canopy(:) = 0._r8 - site_in%fmort_carbonflux_ustory(:) = 0._r8 - site_in%fmort_rate_cambial(:,:) = 0._r8 - site_in%fmort_rate_crown(:,:) = 0._r8 - site_in%rxfmort_rate_canopy(:,:) = 0._r8 - site_in%rxfmort_rate_ustory(:,:) = 0._r8 - site_in%rxfmort_carbonflux_ustory(:) = 0._r8 - site_in%rxfmort_carbonflux_canopy(:) = 0._r8 - site_in%rxfmort_rate_cambial(:,:) = 0._r8 - site_in%rxfmort_rate_crown(:,:) = 0._r8 + site_in%nonrx_fmort_rate_canopy(:,:) = 0._r8 + site_in%nonrx_fmort_rate_ustory(:,:) = 0._r8 + site_in%nonrx_fmort_carbonflux_canopy(:) = 0._r8 + site_in%nonrx_fmort_carbonflux_ustory(:) = 0._r8 + site_in%nonrx_fmort_rate_cambial(:,:) = 0._r8 + site_in%nonrx_fmort_rate_crown(:,:) = 0._r8 + site_in%rx_fmort_rate_canopy(:,:) = 0._r8 + site_in%rx_fmort_rate_ustory(:,:) = 0._r8 + site_in%rx_fmort_carbonflux_ustory(:) = 0._r8 + site_in%rx_fmort_carbonflux_canopy(:) = 0._r8 + site_in%rx_fmort_rate_cambial(:,:) = 0._r8 + site_in%rx_fmort_rate_crown(:,:) = 0._r8 site_in%term_abg_flux(:,:) = 0._r8 site_in%imort_abg_flux(:,:) = 0._r8 - site_in%fmort_abg_flux(:,:) = 0._r8 - site_in%rxfmort_abg_flux(:,:) = 0._r8 + site_in%nonrx_fmort_abg_flux(:,:) = 0._r8 + site_in%rx_fmort_abg_flux(:,:) = 0._r8 ! fusoin-induced growth flux of individuals site_in%growthflux_fusion(:,:) = 0._r8 @@ -374,14 +373,14 @@ subroutine zero_site( site_in ) site_in%term_cflux_ustory_damage(:,:) = 0._r8 site_in%crownarea_canopy_damage = 0._r8 site_in%crownarea_ustory_damage = 0._r8 - site_in%fmort_rate_canopy_damage(:,:,:) = 0._r8 - site_in%fmort_rate_ustory_damage(:,:,:) = 0._r8 - site_in%fmort_cflux_canopy_damage(:,:) = 0._r8 - site_in%fmort_cflux_ustory_damage(:,:) = 0._r8 - site_in%rxfmort_rate_canopy_damage(:,:,:) = 0._r8 - site_in%rxfmort_rate_ustory_damage(:,:,:) = 0._r8 - site_in%rxfmort_cflux_canopy_damage(:,:) = 0._r8 - site_in%rxfmort_cflux_ustory_damage(:,:) = 0._r8 + site_in%nonrx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%nonrx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%nonrx_fmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%nonrx_fmort_cflux_ustory_damage(:,:) = 0._r8 + site_in%rx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%rx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%rx_fmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%rx_fmort_cflux_ustory_damage(:,:) = 0._r8 ! Resources management (logging/harvesting, etc) site_in%resources_management%harvest_debt = 0.0_r8 From 157c5f6c3ba9f1f8c710cdd5cabfcde885d101e0 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 11:31:33 -0700 Subject: [PATCH 036/194] rename patch level fire occurence and burn frac variables --- main/EDInitMod.F90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 281e6b84ec..4fe701c24c 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -1036,9 +1036,12 @@ subroutine init_patches( nsites, sites, bc_in) currentPatch%ros_back = 0._r8 currentPatch%scorch_ht(:) = 0._r8 currentPatch%frac_burnt = 0._r8 - currentPatch%rxfire = 0 - currentPatch%rxfire_fi = 0._r8 - currentPatch%rxfire_frac_burnt = 0._r8 + currentPatch%nonrx_fire = 0 + currentPatch%nonrx_frac_burnt = 0._r8 + currentPatch%nonrx_fi = 0._r8 + currentPatch%rx_fire = 0 + currentPatch%rx_fi = 0._r8 + currentPatch%rx_frac_burnt = 0._r8 currentPatch => currentPatch%older enddo From 278ea3e7cae10eac9f8acd2e47f698f03f96f094 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 11:42:19 -0700 Subject: [PATCH 037/194] correct variable name --- main/FatesRestartInterfaceMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index e10487dca0..e7f7c99430 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -2422,7 +2422,7 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termcflux_usto_sipft(io_idx_si_pft_term) = sites(s)%term_carbonflux_ustory(i_term_type,i_pft) io_idx_si_pft_term = io_idx_si_pft_term + 1 end do - rio_nonrx_cflux_cano_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) + rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_canopy(i_pft) rio_rx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_ustory(i_pft) From 2ec7a1bddce63fe74250089d0cfca2bde42defdd Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 20 Mar 2025 14:41:29 -0700 Subject: [PATCH 038/194] add variables to represent the sum of rx and wildfire --- main/EDInitMod.F90 | 28 +++++++ main/FatesRestartInterfaceMod.F90 | 130 ++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 4fe701c24c..c330661fb6 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -146,6 +146,10 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%demotion_rate(1:nlevsclass)) allocate(site_in%promotion_rate(1:nlevsclass)) allocate(site_in%imort_rate(1:nlevsclass,1:numpft)) + allocate(site_in%fmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%fmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%fmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%fmort_rate_crown(1:nlevsclass,1:numpft)) allocate(site_in%nonrx_fmort_rate_canopy(1:nlevsclass,1:numpft)) allocate(site_in%nonrx_fmort_rate_ustory(1:nlevsclass,1:numpft)) allocate(site_in%nonrx_fmort_rate_cambial(1:nlevsclass,1:numpft)) @@ -165,6 +169,10 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_cflux_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%term_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%term_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) allocate(site_in%nonrx_fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) allocate(site_in%nonrx_fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) allocate(site_in%nonrx_fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) @@ -180,6 +188,10 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_cflux_damage(1,1)) allocate(site_in%term_cflux_canopy_damage(1,1)) allocate(site_in%term_cflux_ustory_damage(1,1)) + allocate(site_in%fmort_rate_canopy_damage(1,1,1)) + allocate(site_in%fmort_rate_ustory_damage(1,1,1)) + allocate(site_in%fmort_cflux_canopy_damage(1,1)) + allocate(site_in%fmort_cflux_ustory_damage(1,1)) allocate(site_in%nonrx_fmort_rate_canopy_damage(1,1,1)) allocate(site_in%nonrx_fmort_rate_ustory_damage(1,1,1)) allocate(site_in%nonrx_fmort_cflux_canopy_damage(1,1)) @@ -193,6 +205,8 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%term_carbonflux_canopy(1:n_term_mort_types,1:numpft)) allocate(site_in%term_carbonflux_ustory(1:n_term_mort_types,1:numpft)) allocate(site_in%imort_carbonflux(1:numpft)) + allocate(site_in%fmort_carbonflux_canopy(1:numpft)) + allocate(site_in%fmort_carbonflux_ustory(1:numpft)) allocate(site_in%nonrx_fmort_carbonflux_canopy(1:numpft)) allocate(site_in%nonrx_fmort_carbonflux_ustory(1:numpft)) allocate(site_in%rx_fmort_carbonflux_canopy(1:numpft)) @@ -200,6 +214,7 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%term_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%imort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%fmort_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%nonrx_fmort_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%rx_fmort_abg_flux(1:nlevsclass,1:numpft)) @@ -329,6 +344,8 @@ subroutine zero_site( site_in ) site_in%term_crownarea_canopy = 0._r8 site_in%term_crownarea_ustory = 0._r8 site_in%imort_crownarea = 0._r8 + site_in%fmort_crownarea_canopy = 0._r8 + site_in%fmort_crownarea_ustory = 0._r8 site_in%nonrx_fmort_crownarea_canopy = 0._r8 site_in%nonrx_fmort_crownarea_ustory = 0._r8 site_in%rx_fmort_crownarea_canopy = 0._r8 @@ -338,6 +355,12 @@ subroutine zero_site( site_in ) site_in%recruitment_rate(:) = 0._r8 site_in%imort_rate(:,:) = 0._r8 site_in%imort_carbonflux(:) = 0._r8 + site_in%fmort_rate_canopy(:,:) = 0._r8 + site_in%fmort_rate_ustory(:,:) = 0._r8 + site_in%fmort_carbonflux_canopy(:) = 0._r8 + site_in%fmort_carbonflux_ustory(:) = 0._r8 + site_in%fmort_rate_cambial(:,:) = 0._r8 + site_in%fmort_rate_crown(:,:) = 0._r8 site_in%nonrx_fmort_rate_canopy(:,:) = 0._r8 site_in%nonrx_fmort_rate_ustory(:,:) = 0._r8 site_in%nonrx_fmort_carbonflux_canopy(:) = 0._r8 @@ -352,6 +375,7 @@ subroutine zero_site( site_in ) site_in%rx_fmort_rate_crown(:,:) = 0._r8 site_in%term_abg_flux(:,:) = 0._r8 site_in%imort_abg_flux(:,:) = 0._r8 + site_in%fmort_abg_flux(:,:) = 0._r8 site_in%nonrx_fmort_abg_flux(:,:) = 0._r8 site_in%rx_fmort_abg_flux(:,:) = 0._r8 @@ -373,6 +397,10 @@ subroutine zero_site( site_in ) site_in%term_cflux_ustory_damage(:,:) = 0._r8 site_in%crownarea_canopy_damage = 0._r8 site_in%crownarea_ustory_damage = 0._r8 + site_in%fmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%fmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%fmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%fmort_cflux_ustory_damage(:,:) = 0._r8 site_in%nonrx_fmort_rate_canopy_damage(:,:,:) = 0._r8 site_in%nonrx_fmort_rate_ustory_damage(:,:,:) = 0._r8 site_in%nonrx_fmort_cflux_canopy_damage(:,:) = 0._r8 diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index e7f7c99430..4f50378f6a 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -240,11 +240,15 @@ module FatesRestartInterfaceMod integer :: ir_recrate_sift integer :: ir_use_this_pft_sift integer :: ir_area_pft_sift + integer :: ir_fmortrate_cano_siscpf + integer :: ir_fmortrate_usto_siscpf integer :: ir_nonrx_fmortrate_cano_siscpf integer :: ir_nonrx_fmortrate_usto_siscpf integer :: ir_rx_fmortrate_cano_siscpf integer :: ir_rx_fmortrate_usto_siscpf integer :: ir_imortrate_siscpf + integer :: ir_fmortrate_crown_siscpf + integer :: ir_fmortrate_cambi_siscpf integer :: ir_nonrx_fmortrate_crown_siscpf integer :: ir_nonrx_fmortrate_cambi_siscpf integer :: ir_rx_fmortrate_crown_siscpf @@ -258,6 +262,8 @@ module FatesRestartInterfaceMod integer :: ir_termcarea_usto_si integer :: ir_imortcarea_si + integer :: ir_fmortcarea_cano_si + integer :: ir_fmortcarea_usto_si integer :: ir_nonrx_fmortcarea_cano_si integer :: ir_nonrx_fmortcarea_usto_si integer :: ir_rx_fmortcarea_cano_si @@ -267,12 +273,15 @@ module FatesRestartInterfaceMod integer :: ir_democflux_si integer :: ir_promcflux_si integer :: ir_imortcflux_sipft + integer :: ir_fmortcflux_cano_sipft + integer :: ir_fmortcflux_usto_sipft integer :: ir_nonrx_fmortcflux_cano_sipft integer :: ir_nonrx_fmortcflux_usto_sipft integer :: ir_rx_fmortcflux_cano_sipft integer :: ir_rx_fmortcflux_usto_sipft integer :: ir_abg_term_flux_siscpf integer :: ir_abg_imort_flux_siscpf + integer :: ir_abg_fmort_flux_siscpf integer :: ir_abg_nonrx_fmort_flux_siscpf integer :: ir_abg_rx_fmort_flux_siscpf @@ -300,6 +309,8 @@ module FatesRestartInterfaceMod integer :: ir_imortrate_sicdpf integer :: ir_termnindiv_cano_sicdpf integer :: ir_termnindiv_usto_sicdpf + integer :: ir_fmortrate_cano_sicdpf + integer :: ir_fmortrate_usto_sicdpf integer :: ir_nonrx_fmortrate_cano_sicdpf integer :: ir_nonrx_fmortrate_usto_sicdpf integer :: ir_rx_fmortrate_cano_sicdpf @@ -307,6 +318,8 @@ module FatesRestartInterfaceMod integer :: ir_imortcflux_sicdsc integer :: ir_termcflux_cano_sicdsc integer :: ir_termcflux_usto_sicdsc + integer :: ir_fmortcflux_cano_sicdsc + integer :: ir_fmortcflux_usto_sicdsc integer :: ir_nonrx_fmortcflux_cano_sicdsc integer :: ir_nonrx_fmortcflux_usto_sicdsc integer :: ir_rx_fmortcflux_cano_sicdsc @@ -1390,6 +1403,16 @@ subroutine define_restart_vars(this, initialize_variables) units='kg', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_seed_out_sift ) + call this%set_restart_var(vname='fates_fmortrate_canopy', vtype=cohort_r8, & + long_name='fates diagnostics on total fire mortality canopy', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_siscpf) + + call this%set_restart_var(vname='fates_fmortrate_ustory', vtype=cohort_r8, & + long_name='fates diagnostics on total fire mortality ustory', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_siscpf) + call this%set_restart_var(vname='fates_nonrx_fmortrate_canopy', vtype=cohort_r8, & long_name='fates diagnostics on wildfire mortality canopy', & units='indiv/ha/year', flushval = flushzero, & @@ -1414,6 +1437,16 @@ subroutine define_restart_vars(this, initialize_variables) long_name='fates diagnostics on impact mortality', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortrate_siscpf) + + call this%set_restart_var(vname='fates_fmortrate_crown', vtype=cohort_r8, & + long_name='fates diagnostics on total crown fire mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_crown_siscpf) + + call this%set_restart_var(vname='fates_fmortrate_cambi', vtype=cohort_r8, & + long_name='fates diagnostics on total fire cambial mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cambi_siscpf) call this%set_restart_var(vname='fates_nonrx_fmortrate_crown', vtype=cohort_r8, & long_name='fates diagnostics on crown fire mortality for wildfire', & @@ -1470,6 +1503,16 @@ subroutine define_restart_vars(this, initialize_variables) units='m2/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcarea_si) + call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & + long_name='fates diagnostic biomass of canopy fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sipft) + + call this%set_restart_var(vname='fates_fmortcflux_ustory', vtype=cohort_r8, & + long_name='fates diagnostic biomass of understory fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sipft) + call this%set_restart_var(vname='fates_nonrx_fmortcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic biomass of canopy wildfire', & units='gC/m2/sec', flushval = flushzero, & @@ -1510,6 +1553,11 @@ subroutine define_restart_vars(this, initialize_variables) units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_imort_flux_siscpf ) + call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & + long_name='fates aboveground biomass loss from fire mortality', & + units='', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_fmort_flux_siscpf ) + call this%set_restart_var(vname='fates_abg_nonrx_fmort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from wildfire mortality', & units='', flushval = flushzero, & @@ -1530,6 +1578,16 @@ subroutine define_restart_vars(this, initialize_variables) units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_promcflux_si ) + call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & + long_name='fates diagnostic crownarea of canopy fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_cano_si) + + call this%set_restart_var(vname='fates_fmortcarea_ustory', vtype=site_r8, & + long_name='fates diagnostic crownarea of understory fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_usto_si) + call this%set_restart_var(vname='fates_nonrx_fmortcarea_canopy', vtype=site_r8, & long_name='fates diagnostic crownarea of canopy wildfire', & units='m2/sec', flushval = flushzero, & @@ -1576,6 +1634,16 @@ subroutine define_restart_vars(this, initialize_variables) units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termnindiv_usto_sicdpf) + call this%set_restart_var(vname='fates_fmortrate_cano_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_sicdpf) + + call this%set_restart_var(vname='fates_fmortrate_usto_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_sicdpf) + call this%set_restart_var(vname='fates_nonrx_fmortrate_cano_dam', vtype=cohort_r8, & long_name='fates diagnostics on wildfire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & @@ -1611,6 +1679,16 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_fmortcflux_cano_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sicdsc) + + call this%set_restart_var(vname='fates_fmortcflux_usto_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_nonrx_fmortcflux_cano_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to wildfire mort by damage class', & units='kgC/ha/day', flushval = flushzero, & @@ -2268,11 +2346,15 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_area_pft_sift => this%rvars(ir_area_pft_sift)%r81d, & rio_seed_in_sift => this%rvars(ir_seed_in_sift)%r81d, & rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & + rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & + rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & + rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & + rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & @@ -2286,6 +2368,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termcarea_usto_si => this%rvars(ir_termcarea_usto_si)%r81d, & rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & + rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & + rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & @@ -2295,11 +2379,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_democflux_si => this%rvars(ir_democflux_si)%r81d, & rio_promcflux_si => this%rvars(ir_promcflux_si)%r81d, & rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & + rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & + rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & rio_rx_fmortcflux_cano_sipft => this%rvars(ir_rx_fmortcflux_cano_sipft)%r81d, & rio_rx_fmortcflux_usto_sipft => this%rvars(ir_rx_fmortcflux_usto_sipft)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & + rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & @@ -2312,6 +2399,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termnindiv_cano_sicdpf => this%rvars(ir_termnindiv_cano_sicdpf)%r81d, & rio_termcflux_usto_sicdsc => this%rvars(ir_termcflux_usto_sicdsc)%r81d, & rio_termnindiv_usto_sicdpf => this%rvars(ir_termnindiv_usto_sicdpf)%r81d, & + rio_fmortrate_cano_sicdpf => this%rvars(ir_fmortrate_cano_sicdpf)%r81d, & + rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & + rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & + rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & @@ -2393,9 +2484,13 @@ subroutine set_restart_vectors(this,nc,nsites,sites) do i_scls = 1, nlevsclass do i_pft = 1, numpft + rio_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_canopy(i_scls, i_pft) + rio_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_ustory(i_scls, i_pft) rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) rio_imortrate_siscpf(io_idx_si_scpf) = sites(s)%imort_rate(i_scls, i_pft) + rio_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_crown(i_scls, i_pft) + rio_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_cambial(i_scls, i_pft) rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) @@ -2405,6 +2500,7 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_growflx_fusion_siscpf(io_idx_si_scpf) = sites(s)%growthflux_fusion(i_scls, i_pft) rio_abg_term_flux_siscpf(io_idx_si_scpf) = sites(s)%term_abg_flux(i_scls, i_pft) rio_abg_imort_flux_siscpf(io_idx_si_scpf) = sites(s)%imort_abg_flux(i_scls, i_pft) + rio_abg_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%fmort_abg_flux(i_scls, i_pft) rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_abg_flux(i_scls, i_pft) rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_abg_flux(i_scls, i_pft) io_idx_si_scpf = io_idx_si_scpf + 1 @@ -2422,6 +2518,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_termcflux_usto_sipft(io_idx_si_pft_term) = sites(s)%term_carbonflux_ustory(i_term_type,i_pft) io_idx_si_pft_term = io_idx_si_pft_term + 1 end do + rio_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_canopy(i_pft) + rio_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_ustory(i_pft) rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_canopy(i_pft) @@ -2816,6 +2914,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcflux_sicdsc(io_idx_si_cdsc) = sites(s)%imort_cflux_damage(i_cdam, i_scls) rio_termcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%term_cflux_canopy_damage(i_cdam, i_scls) rio_termcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%term_cflux_ustory_damage(i_cdam, i_scls) + rio_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) + rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) @@ -2840,6 +2942,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_promcflux_si(io_idx_si) = sites(s)%promotion_carbonflux rio_imortcarea_si(io_idx_si) = sites(s)%imort_crownarea + rio_fmortcarea_cano_si(io_idx_si) = sites(s)%fmort_crownarea_canopy + rio_fmortcarea_usto_si(io_idx_si) = sites(s)%fmort_crownarea_ustory rio_nonrx_fmortcarea_cano_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_canopy rio_nonrx_fmortcarea_usto_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_ustory rio_rx_fmortcarea_cano_si(io_idx_si) = sites(s)%rx_fmort_crownarea_canopy @@ -3302,11 +3406,15 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_area_pft_sift => this%rvars(ir_area_pft_sift)%r81d,& rio_seed_in_sift => this%rvars(ir_seed_in_sift)%r81d, & rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & + rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & + rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & + rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & + rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & @@ -3324,6 +3432,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_termcarea_cano_si => this%rvars(ir_termcarea_cano_si)%r81d, & rio_termcarea_usto_si => this%rvars(ir_termcarea_usto_si)%r81d, & rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & + rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & + rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & @@ -3334,6 +3444,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_imortcflux_sicdsc => this%rvars(ir_imortcflux_sicdsc)%r81d, & rio_termcflux_cano_sicdsc => this%rvars(ir_termcflux_cano_sicdsc)%r81d, & rio_termcflux_usto_sicdsc => this%rvars(ir_termcflux_usto_sicdsc)%r81d, & + rio_fmortrate_cano_sicdpf => this%rvars(ir_fmortrate_cano_sicdpf)%r81d, & + rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & + rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & + rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & @@ -3348,10 +3462,13 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d, & rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & + rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & + rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & + rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d ) @@ -3414,11 +3531,15 @@ subroutine get_restart_vectors(this, nc, nsites, sites) do i_scls = 1,nlevsclass do i_pft = 1, numpft + sites(s)%fmort_rate_canopy(i_scls, i_pft) = rio_fmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%fmort_rate_ustory(i_scls, i_pft) = rio_fmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) = rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) = rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) = rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) sites(s)%rx_fmort_rate_ustory(i_scls, i_pft) = rio_rx_fmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%imort_rate(i_scls, i_pft) = rio_imortrate_siscpf(io_idx_si_scpf) + sites(s)%fmort_rate_crown(i_scls, i_pft) = rio_fmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%fmort_rate_cambial(i_scls, i_pft) = rio_fmortrate_cambi_siscpf(io_idx_si_scpf) sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) = rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) = rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) sites(s)%rx_fmort_rate_crown(i_scls, i_pft) = rio_rx_fmortrate_crown_siscpf(io_idx_si_scpf) @@ -3426,6 +3547,7 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%growthflux_fusion(i_scls, i_pft) = rio_growflx_fusion_siscpf(io_idx_si_scpf) sites(s)%term_abg_flux(i_scls,i_pft) = rio_abg_term_flux_siscpf(io_idx_si_scpf) sites(s)%imort_abg_flux(i_scls,i_pft) = rio_abg_imort_flux_siscpf(io_idx_si_scpf) + sites(s)%fmort_abg_flux(i_scls,i_pft) = rio_abg_fmort_flux_siscpf(io_idx_si_scpf) sites(s)%nonrx_fmort_abg_flux(i_scls,i_pft) = rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) sites(s)%rx_fmort_abg_flux(i_scls,i_pft) = rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) io_idx_si_scpf = io_idx_si_scpf + 1 @@ -3443,6 +3565,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%term_carbonflux_ustory(i_term_type,i_pft) = rio_termcflux_usto_sipft(io_idx_si_pft_term) io_idx_si_pft_term = io_idx_si_pft_term + 1 end do + sites(s)%fmort_carbonflux_canopy(i_pft) = rio_fmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%fmort_carbonflux_ustory(i_pft) = rio_fmortcflux_usto_sipft(io_idx_si_pft) sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) = rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) = rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) sites(s)%rx_fmort_carbonflux_canopy(i_pft) = rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) @@ -3875,6 +3999,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%imort_cflux_damage(i_cdam, i_scls) = rio_imortcflux_sicdsc(io_idx_si_cdsc) sites(s)%term_cflux_canopy_damage(i_cdam, i_scls) = rio_termcflux_cano_sicdsc(io_idx_si_cdsc) sites(s)%term_cflux_ustory_damage(i_cdam, i_scls) = rio_termcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) @@ -3899,6 +4027,8 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%term_crownarea_canopy = rio_termcarea_cano_si(io_idx_si) sites(s)%term_crownarea_ustory = rio_termcarea_usto_si(io_idx_si) sites(s)%imort_crownarea = rio_imortcarea_si(io_idx_si) + sites(s)%fmort_crownarea_canopy = rio_fmortcarea_cano_si(io_idx_si) + sites(s)%fmort_crownarea_ustory = rio_fmortcarea_usto_si(io_idx_si) sites(s)%nonrx_fmort_crownarea_canopy = rio_nonrx_fmortcarea_cano_si(io_idx_si) sites(s)%nonrx_fmort_crownarea_ustory = rio_nonrx_fmortcarea_usto_si(io_idx_si) sites(s)%rx_fmort_crownarea_canopy = rio_rx_fmortcarea_cano_si(io_idx_si) From 7ea4ad816cf4a5d148464c5505e219a81146b5e9 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 24 Mar 2025 14:13:06 -0700 Subject: [PATCH 039/194] update logic to reduce code duplication --- parteh/PRTAllometricCNPMod.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index 3f06ac08a6..a04474df8a 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -1907,9 +1907,8 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! turn on the dynamic L2FR post supplemental N period call get_curr_date(yr, mon, day, sec) - if (spinup_state == 1 .and. yr .gt. nyears_ad_carbon_only) then - call this%CNPAdjustFRootTargets(target_c,target_dcdd) - else if (spinup_state /= 1) then + if ((spinup_state == 1 .and. yr .gt. nyears_ad_carbon_only) .or. & + spinup_state /= 1) then call this%CNPAdjustFRootTargets(target_c,target_dcdd) end if From 12bbcdd4c2fbc38d1d631e9dc9b7d9943ee134f1 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 24 Mar 2025 14:17:46 -0700 Subject: [PATCH 040/194] use fates copy of HLM current year for logic check --- parteh/PRTAllometricCNPMod.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index a04474df8a..831f79fa7b 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -69,9 +69,9 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : prescribed_n_uptake use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model + use FatesInterfaceTypesMod, only : hlm_current_year use elm_varctl , only : nyears_ad_carbon_only, spinup_state - use elm_time_manager , only: get_curr_date, get_curr_time_string @@ -1906,8 +1906,7 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! It will also update the target ! turn on the dynamic L2FR post supplemental N period - call get_curr_date(yr, mon, day, sec) - if ((spinup_state == 1 .and. yr .gt. nyears_ad_carbon_only) .or. & + if ((spinup_state == 1 .and. hlm_current_year .gt. nyears_ad_carbon_only) .or. & spinup_state /= 1) then call this%CNPAdjustFRootTargets(target_c,target_dcdd) end if From ff06fd969c5ef072f9d3b9ec21cbfb1cf08e30e8 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 24 Mar 2025 15:41:03 -0700 Subject: [PATCH 041/194] add flag for carbon only period of ELM AD --- main/FatesInterfaceMod.F90 | 5 ++++- main/FatesInterfaceTypesMod.F90 | 1 + parteh/PRTAllometricCNPMod.F90 | 17 ++++++----------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 42cc8a49ed..08d14cda7a 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -1391,7 +1391,8 @@ subroutine SetFatesTime(current_year_in, current_month_in, & current_day_in, current_tod_in, & current_date_in, reference_date_in, & model_day_in, day_of_year_in, & - days_per_year_in, freq_day_in) + days_per_year_in, freq_day_in, & + ad_temp_carbon_flag) ! This subroutine should be called directly from the HLM @@ -1405,6 +1406,7 @@ subroutine SetFatesTime(current_year_in, current_month_in, & integer, intent(in) :: day_of_year_in integer, intent(in) :: days_per_year_in real(r8), intent(in) :: freq_day_in + logical, intent(in) :: ad_temp_carbon_flag hlm_current_year = current_year_in hlm_current_month = current_month_in @@ -1416,6 +1418,7 @@ subroutine SetFatesTime(current_year_in, current_month_in, & hlm_day_of_year = day_of_year_in hlm_days_per_year = days_per_year_in hlm_freq_day = freq_day_in + hlm_ad_temp_carbon = ad_temp_carbon_flag end subroutine SetFatesTime diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 8eff184c9f..ce14ec0791 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -374,6 +374,7 @@ module FatesInterfaceTypesMod ! include a leap real(r8), public :: hlm_freq_day ! fraction of year for daily time-step ! (1/days_per_year_, this is a frequency + logical, public :: hlm_ad_temp_carbon ! Is ELM in temporary carbon only mode during AD spinup? ! ------------------------------------------------------------------------------------- diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index 831f79fa7b..9cdccdbc39 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -69,11 +69,7 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : prescribed_n_uptake use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model - use FatesInterfaceTypesMod, only : hlm_current_year - - use elm_varctl , only : nyears_ad_carbon_only, spinup_state - - + use FatesInterfaceTypesMod, only : hlm_ad_temp_carbon implicit none private @@ -1857,9 +1853,6 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & real(r8) :: canopy_trim integer :: crown_damage - character(len=256) :: dateTimeString - integer :: yr, mon, day, sec - dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval ipft = this%bc_in(acnp_bc_in_id_pft)%ival @@ -1905,9 +1898,11 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! This routine updates the l2fr (leaf 2 fine-root multiplier) variable ! It will also update the target - ! turn on the dynamic L2FR post supplemental N period - if ((spinup_state == 1 .and. hlm_current_year .gt. nyears_ad_carbon_only) .or. & - spinup_state /= 1) then + ! Turn on the dynamic L2FR post supplemental N period + ! If conducting an accelerated decomposition (AD) spinup in ELM with supplemental + ! nutrients we need to avoid turning this on until after the initial carbon only + ! phase of the spinup + if (.not. hlm_ad_temp_carbon) then call this%CNPAdjustFRootTargets(target_c,target_dcdd) end if From 50c869dfcb88c09afac01a0ec9470431e0197a29 Mon Sep 17 00:00:00 2001 From: adrifoster Date: Tue, 25 Mar 2025 11:58:53 -0600 Subject: [PATCH 042/194] some suggestions --- fire/FatesRxFireMod.F90 | 84 +++++++++++++++++++++++ fire/SFFireWeatherMod.F90 | 28 +++----- fire/SFMainMod.F90 | 137 +++++++++++++++----------------------- 3 files changed, 149 insertions(+), 100 deletions(-) create mode 100644 fire/FatesRxFireMod.F90 diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 new file mode 100644 index 0000000000..eecb771344 --- /dev/null +++ b/fire/FatesRxFireMod.F90 @@ -0,0 +1,84 @@ +module FatesRxFireMod + + + ! ============================================================================ + ! Methods to help with prescribed fire + ! ============================================================================ + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : nearzero + + implicit none + private + + public :: is_prescribed_burn + public :: is_wildfire + + logical function is_prescribed_burn(wildfire_FI, wildfire_ignitions, rx_min_FI, & + rx_max_FI, wildfire_FI_thresh) + ! + ! DESCRIPTION: + ! Determines if a prescribed burn is happening + ! + + ! ARGUMENTS: + real(r8), intent(in) :: wildfire_FI ! wildfire fire intensity [kW/m] + real(r8), intent(in) :: wildfire_ignitions ! wildfire ignitions [count/km2/day] + real(r8), intent(in) :: rx_min_FI ! minimum fire energy of prescribed fire [kW/m] + real(r8), intent(in) :: rx_max_FI ! maximum fire energy of prescribed fire [kW/m] + real(r8), intent(in) :: wildfire_FI_thresh ! threshold for fires that spread or go out [kW/m] + + ! LOCALS: + logical :: rx_man ! prescribed fire using human ignitions + logical :: rx_hyb ! prescribed fire due to both lightning strike and human ignitions + logical :: within_rx_FI_range ! fire intensity is within prescribed burn limits + + ! check if fire intensity falls within prescribed burn range + within_rx_FI_range = wildfire_FI > rx_min_FI .and. wildfire_FI < rx_max_FI + + ! condition for prescribed burn solely due to human ignitions + rx_man = within_rx_FI_range .and. wildfire_ignitions < nearzero + + ! condition for hybrid prescribed burn (low-intensity fire + human ignitions) + rx_hyb = within_rx_FI_range .and. wildfire_FI < wildfire_FI_thresh .and. & + wildfire_ignitions > nearzero + + is_prescribed_burn = rx_man .or. rx_hyb + + end logical function is_prescribed_burn + + !--------------------------------------------------------------------------------------- + + logical function is_wild_fire(wildfire_FI, wildfire_ignitions, rxfire_maxFI, & + wildfire_intensity_thresh) + ! + ! DESCRIPTION: + ! Determines if a wildfire is happening + ! + + ! ARGUMENTS: + real(r8), intent(in) :: wildfire_FI ! wildfire fire intensity [kW/m] + real(r8), intent(in) :: wildfire_ignitions ! wildfire ignitions [count/km2/day] + real(r8), intent(in) :: rx_max_FI ! maximum fire energy of prescribed fire [kW/m] + real(r8), intent(in) :: wildfire_FI_thresh ! threshold for fires that spread or go out [kW/m] + + ! LOCALS: + logical :: managed_wildfire ! is it a wildfire with FI lower than the max rxfire intensity? [can either be Rx fire or wildfire] + logical :: true_wildfire ! is it a wildfire that cannot be managed? + logical :: has_ignitions ! any natural ignitions at the site? + logical :: above_wildfire_thresh ! above the wildfire energy threshold + + has_ignitions = wildfire_ignitions > nearzero + above_wildfire_thresh = wildfire_FI > wildfire_FI_thresh + + managed_wildfire = has_ignitions .and. above_wildfire_thresh .and. & + wildfire_FI < rx_max_FI + + true_wildfire = has_ignitions .and. above_wildfire_thresh .and. & + wildfire_FI > rx_max_FI + + is_wildfire = managed_wildfire .or. true_wildfire + + end logical function is_wild_fire + +end module FatesRxFireMod \ No newline at end of file diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 3f9a05179c..983c50f0e0 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -9,7 +9,7 @@ module SFFireWeatherMod real(r8) :: fire_weather_index ! fire weather index real(r8) :: effective_windspeed ! effective wind speed, corrected for by tree/grass cover [m/min] - integer :: rx_flag ! prescribed fire burn window flag[1=burn window present; 0=no burn window] + integer :: rx_flag ! prescribed fire burn window flag [1=burn window present; 0=no burn window] contains @@ -69,7 +69,7 @@ subroutine UpdateEffectiveWindSpeed(this, wind_speed, tree_fraction, grass_fract end subroutine UpdateEffectiveWindSpeed - subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up, & + subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up, & temp_low,rh_up, rh_low, wind_up, wind_low) ! ARGUMENTS @@ -85,12 +85,12 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up real(r8), intent(in) :: wind_up ! user defined upper bound for wind speed real(r8), intent(in) :: wind_low ! user defined lower bound for wind speed - !LOCAL VARIABLES - real(r8) :: t_check !intermediate value derived from temp condition check - real(r8) :: rh_check !intermediate value derived from RH condition check - real(r8) :: ws_check !intermediate value derived from wind speed condition check + ! LOCAL VARIABLES + real(r8) :: t_check ! intermediate value derived from temp condition check + real(r8) :: rh_check ! intermediate value derived from RH condition check + real(r8) :: ws_check ! intermediate value derived from wind speed condition check - if(.not. rxfire_switch) return + if ( .not. rxfire_switch) return ! check if ambient temperature, relative humidity, and wind speed ! are within user defined ranges by comparing current weather @@ -98,12 +98,11 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up ! it should result in negative value or zero (at the boundary condition) ! for each check below - t_check = (temp_C - temp_low)*(temp_C - temp_up) - rh_check = (rh - rh_low)*(rh - rh_up) - ws_check = (wind - wind_low)*(wind - wind_up) + t_check = (temp_C - temp_low)*(temp_C - temp_up) + rh_check = (rh - rh_low)*(rh - rh_up) + ws_check = (wind - wind_low)*(wind - wind_up) - if(t_check .le. 0.0_r8 .and. rh_check .le. 0.0_r8 .and. & - ws_check .le. 0.0_r8)then + if (t_check <= 0.0_r8 .and. rh_check <= 0.0_r8 .and. ws_check <= 0.0_r8) then this%rx_flag = 1 else this%rx_flag = 0 @@ -111,9 +110,4 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up end subroutine UpdateRxfireBurnWindow - - - - - end module SFFireWeatherMod \ No newline at end of file diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index c28c51870c..7108ffd16b 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -35,20 +35,16 @@ module SFMainMod use FatesInterfaceTypesMod, only : numpft use FatesAllometryMod, only : CrownDepth use FatesFuelClassesMod, only : fuel_classes - implicit none private - - character(len=*), parameter, private :: sourcefile = & - __FILE__ - - + public :: DailyFireModel public :: UpdateFuelCharacteristics - integer :: write_SF = ifalse ! for debugging - logical :: debug = .false. ! for debugging + integer :: write_SF = ifalse ! for debugging + logical :: debug = .false. ! for debugging + character(len=*), parameter, private :: sourcefile = __FILE__ ! ====================================================================================== @@ -142,9 +138,8 @@ subroutine UpdateFireWeather(currentSite, bc_in) ! update prescribed fire burn window call currentSite%fireWeather%UpdateRxfireBurnWindow(rxfire_switch, temp_C, rh, wind, & - SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, SF_val_rxfire_rhlw, & - SF_val_rxfire_wdup, SF_val_rxfire_wdlw) - + SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, SF_val_rxfire_rhlw, & + SF_val_rxfire_wdup, SF_val_rxfire_wdlw) ! calculate site-level tree, grass, and bare fraction call CalculateTreeGrassAreaSite(currentSite, tree_fraction, grass_fraction, bare_fraction) @@ -153,7 +148,6 @@ subroutine UpdateFireWeather(currentSite, bc_in) call currentSite%fireWeather%UpdateEffectiveWindSpeed(wind*sec_per_min, tree_fraction, & grass_fraction, bare_fraction) - end subroutine UpdateFireWeather !--------------------------------------------------------------------------------------- @@ -254,7 +248,7 @@ subroutine CalculateIgnitionsandFDI(currentSite, bc_in) ! if the oldest patch is a bareground patch (i.e. nocomp mode is on) use the first vegetated patch ! for the iofp index (i.e. the next younger patch) currentPatch => currentSite%oldest_patch - if(currentPatch%nocomp_pft_label .eq. nocomp_bareground)then + if (currentPatch%nocomp_pft_label == nocomp_bareground)then currentPatch => currentPatch%younger endif iofp = currentPatch%patchno @@ -373,9 +367,11 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! use SFEquationsMod, only : FireIntensity - use SFParamsMod, only : SF_val_fire_threshold, SF_val_rxfire_minthreshold, & - SF_val_rxfire_maxthreshold, SF_val_rxfire_fuel_min, SF_val_rxfire_fuel_max + use SFParamsMod, only : SF_val_fire_threshold, SF_val_rxfire_minthreshold + use SFParamsMod, only : SF_val_rxfire_maxthreshold, SF_val_rxfire_fuel_min + use SFParamsMod, only : SF_val_rxfire_fuel_max use EDParamsMod, only : rxfire_switch + use FatesRxFireMod, only : is_prescribed_burn, is_wild_fire ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -384,11 +380,8 @@ subroutine CalculateSurfaceFireIntensity(currentSite) type(fates_patch_type), pointer :: currentPatch ! patch object real(r8) :: fuel_consumed(num_fuel_classes) ! fuel consumed [kgC/m2] logical :: is_rxfire ! is it a prescribed fire? - logical :: rx_man ! prescribed fire use human ignition - logical :: rx_hyb ! prescribed fire due to both lightning strike and human ignition - logical :: managed_wildfire ! is it a wildfire with FI lower than the max rxfire intensity?[can either be Rx fire or wildfire] - logical :: true_wildfire ! is it a wildfire that cannot be managed? logical :: is_wildfire ! combine both managed and true wildfire for now + logical :: rxfire_fuel_check ! is fuel within thresholds for prescribed burn currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -411,56 +404,44 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch%rx_FI = 0.0_r8 currentPatch%nonrx_FI = 0.0_r8 - if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag .eq. itrue) then + if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag == itrue) then ! fire intensity [kW/m] currentPatch%FI = FireIntensity(currentPatch%TFC_ROS/0.45_r8, currentPatch%ROS_front/60.0_r8) - - ! Decide if prescribed fire or wildfire happen - ! prescribed fire and wildfire cannot happen on the same patch - - ! store some contion check here to simplify decision tree - rx_man = (currentPatch%FI > SF_val_rxfire_minthreshold .and. & - currentPatch%FI < SF_val_rxfire_maxthreshold .and. & - currentSite%NF == 0.0_r8) - - rx_hyb = (currentPatch%FI < SF_val_fire_threshold .and. & - currentPatch%FI > SF_val_rxfire_minthreshold .and. & - currentPatch%FI < SF_val_rxfire_maxthreshold .and. & - currentSite%NF > 0.0_r8) + ! check if prescribed fire can occur based on fuel load + rxfire_fuel_check = currentPatch%fuel%non_trunk_loading > SF_val_rxfire_fuel_min .and. & + currentPatch%fuel%non_trunk_loading < SF_val_rxfire_fuel_max - is_rxfire = (rx_man .or. rx_hyb) - - managed_wildfire = (currentSite%NF > 0.0_r8 .and. & - currentPatch%FI > SF_val_fire_threshold .and. & - currentPatch%FI < SF_val_rxfire_maxthreshold) - - true_wildfire = (currentSite%NF > 0.0_r8 .and. & - currentPatch%FI > SF_val_fire_threshold .and. & - currentPatch%FI > SF_val_rxfire_maxthreshold) + if (currentSite%fireWeather%rx_flag == itrue .and. rxfire_fuel_check) then + + ! record burnable area after fuel load check + currentSite%rxfire_area_fuel = currentSite%rxfire_area_fuel + currentPatch%area + + ! determine fire type + ! prescribed fire and wildfire cannot happen on the same patch + is_rxfire = is_prescribed_burn(currentPatch%FI, currentSite%NF, & + SF_val_rxfire_minthreshold, SF_val_rxfire_maxthreshold, SF_val_fire_threshold) - is_wildfire = (managed_wildfire .or. true_wildfire) + is_wildfire = is_wild_fire(currentPatch%FI, currentSite%NF, SF_val_rxfire_minthreshold, & + SF_val_fire_threshold) - if (currentSite%fireWeather%rx_flag == itrue .and. & ! burn window check - currentPatch%fuel%non_trunk_loading > SF_val_rxfire_fuel_min .and. & ! fuel load check - currentPatch%fuel%non_trunk_loading < SF_val_rxfire_fuel_max) then - currentSite%rxfire_area_fuel = currentSite%rxfire_area_fuel + currentPatch%area ! record burnable area after fuel load check if (is_rxfire) then currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check currentPatch%rx_fire = 1 + else if (is_wildfire) then currentPatch%nonrx_fire = 1 end if - + else ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on ! track wildfires greater than kW/m energy threshold if (currentPatch%FI > SF_val_fire_threshold) then currentPatch%nonrx_fire = 1 end if - end if + ! assign fire intensities and ignitions based on fire type if (currentPatch%nonrx_fire == itrue) then currentSite%NF_successful = currentSite%NF_successful + & currentSite%NF*currentSite%FDI*currentPatch%area/area @@ -468,10 +449,8 @@ subroutine CalculateSurfaceFireIntensity(currentSite) else if (currentPatch%rx_fire == itrue) then currentPatch%rx_FI = currentPatch%FI end if - end if end if - currentPatch => currentPatch%younger end do @@ -539,49 +518,46 @@ end subroutine CalculateAreaBurnt !--------------------------------------------------------------------------------------- - !***************************************************************** - subroutine CalculateRxfireAreaBurnt ( currentSite ) - !***************************************************************** - - !returns burned fraction for prescribed fire per patch by first checking - !if total burnable fraction at site level is greater than user defined fraction of site area - !if yes, calculate burned fraction as (user defined frac / total burnable frac) - - use SFParamsMod, only : SF_val_rxfire_AB !user defined prescribed fire area in fraction per day to reflect burning capacity - use SFParamsMod, only : SF_val_rxfire_min_frac ! minimum fraction of land needs to be burnable for conducting prescribed fire + subroutine CalculateRxfireAreaBurnt (currentSite) + ! + ! DESCRIPTION: + ! Returns burned fraction for prescribed fire per patch by first checking + ! if total burnable fraction at site level is greater than user defined fraction of site area + ! if yes, calculate burned fraction as (user defined frac / total burnable frac) + ! + use SFParamsMod, only : SF_val_rxfire_AB ! user defined prescribed fire area in fraction per day to reflect burning capacity + use SFParamsMod, only : SF_val_rxfire_min_frac ! minimum fraction of land needs to be burnable for conducting prescribed fire ! ARGUMENTS type(ed_site_type), intent(inout), target :: currentSite - !LOCALS + ! LOCALS type(fates_patch_type), pointer :: currentPatch + real(r8) :: total_burnable_frac ! total fractional land area that can apply prescribed fire after condition checks at site level - real(r8) :: total_burnable_frac ! total fractional land area that can apply prescribed fire after condition checks at site level - - ! initialize site variables currentSite%rxfire_area_final = 0.0_r8 total_burnable_frac = 0.0_r8 ! update total burnable fraction - total_burnable_frac = currentSite%rxfire_area_fi / AREA + total_burnable_frac = currentSite%rxfire_area_fi/AREA - currentPatch => currentSite%oldest_patch; - - do while(associated(currentPatch)) + currentPatch => currentSite%oldest_patch - if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - currentPatch%fire = 0 ! fire, either rx or non-rx + do while (associated(currentPatch)) + if (currentPatch%nocomp_pft_label /= nocomp_bareground) then + currentPatch%fire = 0 ! fire, either rx or non-rx currentPatch%frac_burnt = 0.0_r8 ! rx_frac_burnt + nonrx_frac_burnt currentPatch%rx_frac_burnt = 0.0_r8 - if (currentPatch%rx_fire .eq. itrue .and. & - total_burnable_frac .ge. SF_val_rxfire_min_frac ) then + if (currentPatch%rx_fire == itrue .and. & + total_burnable_frac >= SF_val_rxfire_min_frac ) then currentSite%rxfire_area_final = currentSite%rxfire_area_final + currentPatch%area ! the final burned total land area - currentPatch%rx_frac_burnt = min(0.99_r8, (SF_val_rxfire_AB / total_burnable_frac)) + currentPatch%rx_frac_burnt = min(0.99_r8, SF_val_rxfire_AB/total_burnable_frac) else currentPatch%rx_fire = 0 ! update rxfire occurence at patch currentPatch%rx_FI = 0.0_r8 end if + ! update patch level fire occurence and total frac burnt currentPatch%fire = currentPatch%nonrx_fire + currentPatch%rx_fire currentPatch%frac_burnt = currentPatch%nonrx_frac_burnt + currentPatch%rx_frac_burnt @@ -590,23 +566,18 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) ! we currently do not allow this to happen on the same patch yet if (currentPatch%fire > 1) then write(fates_log(),*) 'Both wildfire and management fire are happening at same patch' - write(fates_log(),*) 'rxfire =',currentPatch%rx_fire - write(fates_log(),*) 'wildfire =',currentPatch%nonrx_fire + write(fates_log(),*) 'rxfire =', currentPatch%rx_fire + write(fates_log(),*) 'wildfire =', currentPatch%nonrx_fire call endrun(msg=errMsg(sourcefile, __LINE__)) end if - - end if - - currentPatch => currentPatch%younger; - end do ! end patch loop + currentPatch => currentPatch%younger + end do end subroutine CalculateRxfireAreaBurnt - !--------------------------------------------------------------------------------------- - !***************************************************************** subroutine crown_scorching ( currentSite ) !***************************************************************** From 5e634aea2c144d3bc190ad7523f9584fd6768810 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 27 Mar 2025 14:53:41 -0700 Subject: [PATCH 043/194] Revert "add flag for carbon only period of ELM AD" This reverts commit ff06fd969c5ef072f9d3b9ec21cbfb1cf08e30e8. --- main/FatesInterfaceMod.F90 | 5 +---- main/FatesInterfaceTypesMod.F90 | 1 - parteh/PRTAllometricCNPMod.F90 | 17 +++++++++++------ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 08d14cda7a..42cc8a49ed 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -1391,8 +1391,7 @@ subroutine SetFatesTime(current_year_in, current_month_in, & current_day_in, current_tod_in, & current_date_in, reference_date_in, & model_day_in, day_of_year_in, & - days_per_year_in, freq_day_in, & - ad_temp_carbon_flag) + days_per_year_in, freq_day_in) ! This subroutine should be called directly from the HLM @@ -1406,7 +1405,6 @@ subroutine SetFatesTime(current_year_in, current_month_in, & integer, intent(in) :: day_of_year_in integer, intent(in) :: days_per_year_in real(r8), intent(in) :: freq_day_in - logical, intent(in) :: ad_temp_carbon_flag hlm_current_year = current_year_in hlm_current_month = current_month_in @@ -1418,7 +1416,6 @@ subroutine SetFatesTime(current_year_in, current_month_in, & hlm_day_of_year = day_of_year_in hlm_days_per_year = days_per_year_in hlm_freq_day = freq_day_in - hlm_ad_temp_carbon = ad_temp_carbon_flag end subroutine SetFatesTime diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index ce14ec0791..8eff184c9f 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -374,7 +374,6 @@ module FatesInterfaceTypesMod ! include a leap real(r8), public :: hlm_freq_day ! fraction of year for daily time-step ! (1/days_per_year_, this is a frequency - logical, public :: hlm_ad_temp_carbon ! Is ELM in temporary carbon only mode during AD spinup? ! ------------------------------------------------------------------------------------- diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index 9cdccdbc39..831f79fa7b 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -69,7 +69,11 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : prescribed_n_uptake use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model - use FatesInterfaceTypesMod, only : hlm_ad_temp_carbon + use FatesInterfaceTypesMod, only : hlm_current_year + + use elm_varctl , only : nyears_ad_carbon_only, spinup_state + + implicit none private @@ -1853,6 +1857,9 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & real(r8) :: canopy_trim integer :: crown_damage + character(len=256) :: dateTimeString + integer :: yr, mon, day, sec + dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval ipft = this%bc_in(acnp_bc_in_id_pft)%ival @@ -1898,11 +1905,9 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! This routine updates the l2fr (leaf 2 fine-root multiplier) variable ! It will also update the target - ! Turn on the dynamic L2FR post supplemental N period - ! If conducting an accelerated decomposition (AD) spinup in ELM with supplemental - ! nutrients we need to avoid turning this on until after the initial carbon only - ! phase of the spinup - if (.not. hlm_ad_temp_carbon) then + ! turn on the dynamic L2FR post supplemental N period + if ((spinup_state == 1 .and. hlm_current_year .gt. nyears_ad_carbon_only) .or. & + spinup_state /= 1) then call this%CNPAdjustFRootTargets(target_c,target_dcdd) end if From 4e4bf05a62c410354c856f2171ad55aba6cb9111 Mon Sep 17 00:00:00 2001 From: Charlie Koven Date: Thu, 27 Mar 2025 13:55:26 -0700 Subject: [PATCH 044/194] added logic so that seeds only fall on patches with a given nocomp PFT --- biogeochem/EDPhysiologyMod.F90 | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 3b1d61e914..70623eb6db 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2080,6 +2080,10 @@ subroutine SeedUpdate( currentSite ) integer :: el ! loop counter for litter element types integer :: element_id ! element id consistent with parteh/PRTGenericMod.F90 + logical, parameter :: nocomp_seed_localization = .true. ! if nocomp is on, only send a given PFT's seeds to patches of that nocomp PFT + real(r8) :: nocomp_seed_scaling ! scalar to handle case for nocomp_seed_localization + real(r8) :: nocomp_patch_areas(0:numpft) ! vector of the total patch areas for each nocomp PFT + ! If the dispersal kernel is not turned on, keep the dispersal fraction at zero site_disp_frac(:) = 0._r8 if (hlm_seeddisp_cadence .ne. fates_dispersal_cadence_none) then @@ -2093,6 +2097,19 @@ subroutine SeedUpdate( currentSite ) site_mass => currentSite%mass_balance(el) + ! If we are in nocomp configuration and we are restricting each PFT's seeds to all fall + ! only on patches that allow that PFT to grow, then we need to add up all the patch areas + ! for each nocomp PFT to normalize the seed fluxes with later. + if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then + nocomp_patch_areas(0:numpft) = 0.r8 + currentPatch => currentSite%oldest_patch + nocomp_patch_loop: do while (associated(currentPatch)) + nocomp_patch_areas(currentPatch%nocomp_pft_label) = nocomp_patch_areas(currentPatch%nocomp_pft_label) & + + currentPatch%area + currentPatch => currentPatch%younger + end do nocomp_patch_loop + endif + ! Loop over all patches and sum up the seed input for each PFT currentPatch => currentSite%oldest_patch seed_rain_loop: do while (associated(currentPatch)) @@ -2152,9 +2169,23 @@ subroutine SeedUpdate( currentSite ) if(currentSite%use_this_pft(pft).eq.itrue)then + ! special case: do we want to restrict each PFT's seeds to only go to patches with that nocomp PFT label? + ! If so, then use a normalization factor that is one over the nocomp patch fraction for all patches of + ! that PFT's nocomp label, and zero for all other patches. If we don't do this, then just set scalar to one. + if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then + if (currentPatch%nocomp_pft_label .eq. pft) then + nocomp_seed_scaling = AREA/nocomp_patch_areas(pft) + else + nocomp_seed_scaling = 0._r8 + endif + else + nocomp_seed_scaling = 1._r8 + endif + ! Seed input from local sources (within site). Note that a fraction of the ! internal seed rain is sent out to neighboring gridcells. - litt%seed_in_local(pft) = litt%seed_in_local(pft) + site_seed_rain(pft)*(1.0_r8-site_disp_frac(pft))/area ![kg/m2/day] + litt%seed_in_local(pft) = litt%seed_in_local(pft) + nocomp_seed_scaling * & + site_seed_rain(pft)*(1.0_r8-site_disp_frac(pft))/area ![kg/m2/day] ! If we are using the Tree Recruitment Scheme (TRS) with or w/o seedling dynamics if ( any(hlm_regeneration_model == [TRS_regeneration, TRS_no_seedling_dyn]) .and. & From 3f1f177c0324c1e4c568d45e786d7feb8729d456 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Thu, 27 Mar 2025 14:58:48 -0700 Subject: [PATCH 045/194] fixed typo on nocomp seed logic --- biogeochem/EDPhysiologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 70623eb6db..53f60a1a2c 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2101,7 +2101,7 @@ subroutine SeedUpdate( currentSite ) ! only on patches that allow that PFT to grow, then we need to add up all the patch areas ! for each nocomp PFT to normalize the seed fluxes with later. if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then - nocomp_patch_areas(0:numpft) = 0.r8 + nocomp_patch_areas(0:numpft) = 0._r8 currentPatch => currentSite%oldest_patch nocomp_patch_loop: do while (associated(currentPatch)) nocomp_patch_areas(currentPatch%nocomp_pft_label) = nocomp_patch_areas(currentPatch%nocomp_pft_label) & From e69c2f4c26758b8c15db7d4e0860f958d2055b0f Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 27 Mar 2025 15:26:39 -0700 Subject: [PATCH 046/194] add interface to receive supplemental nutrient status from HLM --- main/FatesInterfaceMod.F90 | 26 +++++++++++++++++++++++++- main/FatesInterfaceTypesMod.F90 | 3 +++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 42cc8a49ed..4b7937b909 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -1477,8 +1477,10 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) hlm_nu_com = 'unset' hlm_decomp = 'unset' hlm_nitrogen_spec = unset_int - hlm_use_tree_damage = unset_int hlm_phosphorus_spec = unset_int + hlm_nitrogen_supl = unset_int + hlm_phosphorus_supl = unset_int + hlm_use_tree_damage = unset_int hlm_use_ch4 = unset_int hlm_use_vertsoilc = unset_int hlm_parteh_mode = unset_int @@ -1695,6 +1697,16 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) call endrun(msg=errMsg(sourcefile, __LINE__)) end if + if(hlm_nitrogen_supl .eq. unset_int) then + write(fates_log(),*) 'FATES parameters unset: hlm_nitrogen_supl, exiting' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + + if(hlm_phosphorus_supl .eq. unset_int) then + write(fates_log(),*) 'FATES parameters unset: hlm_phosphorus_supl, exiting' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + if( abs(hlm_hio_ignore_val-unset_double)<1e-10 ) then write(fates_log(),*) 'FATES dimension/parameter unset: hio_ignore' call endrun(msg=errMsg(sourcefile, __LINE__)) @@ -1917,6 +1929,18 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) write(fates_log(),*) 'Transfering hlm_phosphorus_spec = ',ival,' to FATES' end if + case('nitrogen_supl') + hlm_nitrogen_supl = ival + if (fates_global_verbose()) then + write(fates_log(),*) 'Transfering hlm_nitrogen_supl = ',ival,' to FATES' + end if + + case('phosphorus_supl') + hlm_phosphorus_supl = ival + if (fates_global_verbose()) then + write(fates_log(),*) 'Transfering hlm_phosphorus_supl = ',ival,' to FATES' + end if + case('use_ch4') hlm_use_ch4 = ival if (fates_global_verbose()) then diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 8eff184c9f..09829d2ba5 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -69,6 +69,9 @@ module FatesInterfaceTypesMod integer, public :: hlm_phosphorus_spec ! Signals if phosphorous is turned on in the HLM ! 0: none ! 1: p is on + ! + integer, public :: hlm_nitrogen_supl ! HLM nitrogen supplementation status + integer, public :: hlm_phosphorus_supl ! HLM phosphorus supplementation status real(r8), public :: hlm_stepsize ! The step-size of the host land model (s) ! moreover, this is the shortest main-model timestep From 97cb3bbc604ba3233502cc09dcce2a8177ae4e54 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 27 Mar 2025 15:27:04 -0700 Subject: [PATCH 047/194] update check to determine if nutrients are supplemented If nutrients are being supplemented, then the fine root target adjustment will be avoided --- parteh/PRTAllometricCNPMod.F90 | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index 831f79fa7b..cf931d0a66 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -69,10 +69,8 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : prescribed_n_uptake use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model - use FatesInterfaceTypesMod, only : hlm_current_year - - use elm_varctl , only : nyears_ad_carbon_only, spinup_state - + use FatesInterfaceTypesMod, only : hlm_nitrogen_supl + use FatesInterfaceTypesMod, only : hlm_phosphorus_supl implicit none @@ -1848,6 +1846,8 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & real(r8), dimension(num_organs) :: deficit_p real(r8) :: target_n real(r8) :: target_p + logical :: limiting_p + logical :: limiting_n real(r8) :: store_c_target ! Target amount of C in storage including "overflow" [kgC] real(r8) :: total_c_flux ! Total C flux from gains into storage and growth R [kgC] real(r8), pointer :: dbh @@ -1857,9 +1857,6 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & real(r8) :: canopy_trim integer :: crown_damage - character(len=256) :: dateTimeString - integer :: yr, mon, day, sec - dbh => this%bc_inout(acnp_bc_inout_id_dbh)%rval canopy_trim = this%bc_in(acnp_bc_in_id_ctrim)%rval ipft = this%bc_in(acnp_bc_in_id_pft)%ival @@ -1905,11 +1902,13 @@ subroutine CNPAllocateRemainder(this, c_gain,n_gain,p_gain, & ! This routine updates the l2fr (leaf 2 fine-root multiplier) variable ! It will also update the target - ! turn on the dynamic L2FR post supplemental N period - if ((spinup_state == 1 .and. hlm_current_year .gt. nyears_ad_carbon_only) .or. & - spinup_state /= 1) then + ! turn on the dynamic L2FR if either nutrient in not being supplemented + limiting_p = ((p_uptake_mode .eq. coupled_p_uptake) .and. (hlm_phosphorus_supl .eq. ifalse)) + limiting_n = ((n_uptake_mode .eq. coupled_p_uptake) .and. (hlm_nitrogen_supl .eq. ifalse)) + + if (limiting_p .or. limiting_n) then call this%CNPAdjustFRootTargets(target_c,target_dcdd) - end if + end if ! ----------------------------------------------------------------------------------- ! If carbon is still available, lets cram some into storage overflow From 917ff129a5a00ee5070536e3d5f891f1783d0dd8 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Mon, 31 Mar 2025 10:34:05 -0700 Subject: [PATCH 048/194] update fire mortality var name for wildfire and prescribed fire --- biogeochem/EDCohortDynamicsMod.F90 | 8 ++++---- biogeochem/EDPatchDynamicsMod.F90 | 32 +++++++++++++++--------------- biogeochem/FatesCohortMod.F90 | 20 +++++++++---------- fire/SFMainMod.F90 | 8 ++++---- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 983a307653..60af53fb58 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1020,11 +1020,11 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%fire_mort = (currentCohort%n*currentCohort%fire_mort + & nextc%n*nextc%fire_mort)/newn - currentCohort%nonrx_mort = (currentCohort%n*currentCohort%nonrx_mort + & - nextc%n*nextc%nonrx_mort)/newn + currentCohort%nonrx_fire_mort = (currentCohort%n*currentCohort%nonrx_fire_mort + & + nextc%n*nextc%nonrx_fire_mort)/newn - currentCohort%rx_mort = (currentCohort%n*currentCohort%rx_mort + & - nextc%n*nextc%rx_mort)/newn + currentCohort%rx_fire_mort = (currentCohort%n*currentCohort%rx_fire_mort + & + nextc%n*nextc%rx_fire_mort)/newn ! mortality diagnostics currentCohort%cmort = (currentCohort%n*currentCohort%cmort + nextc%n*nextc%cmort)/newn diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index ddc47c8885..1f30a63a70 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -978,11 +978,11 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rx_mort / hlm_freq_day ! for prescribed fire + nc%n * currentCohort%rx_fire_mort / hlm_freq_day ! for prescribed fire currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%nonrx_mort / hlm_freq_day ! for wildfire fire + nc%n * currentCohort%nonrx_fire_mort / hlm_freq_day ! for wildfire fire currentSite%fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%fmort_carbonflux_canopy(currentCohort%pft) + & @@ -991,12 +991,12 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) + & - (nc%n * currentCohort%rx_mort) * & + (nc%n * currentCohort%rx_fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) + & - (nc%n * currentCohort%nonrx_mort) * & + (nc%n * currentCohort%nonrx_fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 else @@ -1007,11 +1007,11 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%rx_mort / hlm_freq_day + nc%n * currentCohort%rx_fire_mort / hlm_freq_day currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%nonrx_mort / hlm_freq_day + nc%n * currentCohort%nonrx_fire_mort / hlm_freq_day currentSite%fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%fmort_carbonflux_ustory(currentCohort%pft) + & @@ -1020,12 +1020,12 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) + & - (nc%n * currentCohort%rx_mort) * & + (nc%n * currentCohort%rx_fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) + & - (nc%n * currentCohort%nonrx_mort) * & + (nc%n * currentCohort%nonrx_fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 @@ -1040,14 +1040,14 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & - (nc%n * currentCohort%rx_mort) * & + (nc%n * currentCohort%rx_fire_mort) * & ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & leaf_c ) * & g_per_kg * days_per_sec * ha_per_m2 currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & - (nc%n * currentCohort%nonrx_mort) * & + (nc%n * currentCohort%nonrx_fire_mort) * & ((sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & leaf_c) * g_per_kg * days_per_sec * ha_per_m2 @@ -1108,16 +1108,16 @@ subroutine spawn_patches( currentSite, bc_in) (leaf_burn_frac > 1._r8) .or. & (currentCohort%fire_mort < 0._r8) .or. & (currentCohort%fire_mort > 1._r8) .or. & - (currentCohort%rx_mort < 0._r8) .or. & - (currentCohort%rx_mort > 1._r8) .or. & - (currentCohort%nonrx_mort < 0._r8) .or. & - (currentCohort%nonrx_mort > 1._r8) ) then + (currentCohort%rx_fire_mort < 0._r8) .or. & + (currentCohort%rx_fire_mort > 1._r8) .or. & + (currentCohort%nonrx_fire_mort < 0._r8) .or. & + (currentCohort%nonrx_fire_mort > 1._r8) ) then write(fates_log(),*) 'unexpected fire fractions' write(fates_log(),*) prt_params%woody(currentCohort%pft) write(fates_log(),*) leaf_burn_frac write(fates_log(),*) currentCohort%fire_mort - write(fates_log(),*) currentCohort%rx_mort - write(fates_log(),*) currentCohort%nonrx_mort + write(fates_log(),*) currentCohort%rx_fire_mort + write(fates_log(),*) currentCohort%nonrx_fire_mort call endrun(msg=errMsg(sourcefile, __LINE__)) end if diff --git a/biogeochem/FatesCohortMod.F90 b/biogeochem/FatesCohortMod.F90 index 3f5073be1b..31a0c696b8 100644 --- a/biogeochem/FatesCohortMod.F90 +++ b/biogeochem/FatesCohortMod.F90 @@ -273,10 +273,10 @@ module FatesCohortMod real(r8) :: fire_mort ! post-fire mortality from cambial and crown damage assuming two are independent [0-1] real(r8) :: nonrx_cambial_mort ! cambial kill mortality due to wildfire real(r8) :: nonrx_crown_mort ! crown fire mortality due to wildfire - real(r8) :: nonrx_mort ! post-fire mortality due to wildfire + real(r8) :: nonrx_fire_mort ! post-fire mortality due to wildfire real(r8) :: rx_cambial_mort ! cambial kill mortality due to prescribed fire real(r8) :: rx_crown_mort ! crown fire mortality due to prescribed fire - real(r8) :: rx_mort ! post-fire mortality due to prescribed fire + real(r8) :: rx_fire_mort ! post-fire mortality due to prescribed fire !--------------------------------------------------------------------------- @@ -457,10 +457,10 @@ subroutine NanValues(this) this%fire_mort = nan this%nonrx_cambial_mort = nan this%nonrx_crown_mort = nan - this%nonrx_mort = nan + this%nonrx_fire_mort = nan this%rx_cambial_mort = nan this%rx_crown_mort = nan - this%rx_mort = nan + this%rx_fire_mort = nan end subroutine NanValues @@ -549,10 +549,10 @@ subroutine ZeroValues(this) this%fire_mort = 0._r8 this%nonrx_cambial_mort = 0._r8 this%nonrx_crown_mort = 0._r8 - this%nonrx_mort = 0._r8 + this%nonrx_fire_mort = 0._r8 this%rx_cambial_mort = 0._r8 this%rx_crown_mort = 0._r8 - this%rx_mort = 0._r8 + this%rx_fire_mort = 0._r8 end subroutine ZeroValues @@ -800,10 +800,10 @@ subroutine Copy(this, copyCohort) copyCohort%fire_mort = this%fire_mort copyCohort%nonrx_cambial_mort = this%nonrx_cambial_mort copyCohort%nonrx_crown_mort = this%nonrx_crown_mort - copyCohort%nonrx_mort = this%nonrx_mort + copyCohort%nonrx_fire_mort = this%nonrx_fire_mort copyCohort%rx_cambial_mort = this%rx_cambial_mort copyCohort%rx_crown_mort = this%rx_crown_mort - copyCohort%rx_mort = this%rx_mort + copyCohort%rx_fire_mort = this%rx_fire_mort ! HYDRAULICS if (hlm_use_planthydro .eq. itrue) then @@ -1106,10 +1106,10 @@ subroutine Dump(this) write(fates_log(),*) 'cohort%cambial_mort = ', this%cambial_mort write(fates_log(),*) 'cohort%nonrx_cambial_mort = ', this%nonrx_cambial_mort write(fates_log(),*) 'cohort%nonrx_crown_mort = ', this%nonrx_crown_mort - write(fates_log(),*) 'cohort%nonrx_mort = ', this%nonrx_mort + write(fates_log(),*) 'cohort%nonrx_fire_mort = ', this%nonrx_fire_mort write(fates_log(),*) 'cohort%rx_crown_mort = ', this%rx_crown_mort write(fates_log(),*) 'cohort%rx_cambial_mort = ', this%rx_cambial_mort - write(fates_log(),*) 'cohort%rx_mort = ', this%rx_mort + write(fates_log(),*) 'cohort%rx_fire_mort = ', this%rx_fire_mort write(fates_log(),*) 'cohort%size_class = ', this%size_class write(fates_log(),*) 'cohort%size_by_pft_class = ', this%size_by_pft_class diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index c28c51870c..7c93656249 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -817,10 +817,10 @@ subroutine post_fire_mortality ( currentSite ) do while(associated(currentCohort)) currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 - currentCohort%nonrx_mort = 0.0_r8 + currentCohort%nonrx_fire_mort = 0.0_r8 currentCohort%nonrx_crown_mort = 0.0_r8 currentCohort%nonrx_cambial_mort = 0.0_r8 - currentCohort%rx_mort = 0.0_r8 + currentCohort%rx_fire_mort = 0.0_r8 currentCohort%rx_crown_mort = 0.0_r8 currentCohort%rx_cambial_mort = 0.0_r8 @@ -836,11 +836,11 @@ subroutine post_fire_mortality ( currentSite ) ! now decide which type of post-fire mortality, prescribed fire or wildfire? if (currentPatch%nonrx_fire == itrue .and. currentPatch%rx_fire == ifalse) then - currentCohort%nonrx_mort = currentCohort%fire_mort + currentCohort%nonrx_fire_mort = currentCohort%fire_mort currentCohort%nonrx_crown_mort = currentCohort%crownfire_mort currentCohort%nonrx_cambial_mort = currentCohort%cambial_mort else - currentCohort%rx_mort = currentCohort%fire_mort + currentCohort%rx_fire_mort = currentCohort%fire_mort currentCohort%rx_crown_mort = currentCohort%crownfire_mort currentCohort%rx_cambial_mort = currentCohort%cambial_mort end if From 96de0b6cfba744a12381053e6c832fb5001e3906 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Mon, 31 Mar 2025 10:43:48 -0700 Subject: [PATCH 049/194] cleab up some white space --- fire/SFMainMod.F90 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 7c93656249..1f4c76d931 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -323,7 +323,7 @@ subroutine CalculateSurfaceRateOfSpread(currentSite) if (beta_op < nearzero) then beta_ratio = 0.0_r8 else - beta_ratio = beta/beta_op + beta_ratio = beta/beta_op end if ! remove mineral content from fuel load per Thonicke 2010 @@ -393,7 +393,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) - currentPatch%fuel%frac_burnt(:) = 0.0_r8 + currentPatch%fuel%frac_burnt(:) = 0.0_r8 if (currentPatch%nocomp_pft_label /= nocomp_bareground) then @@ -409,7 +409,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch%nonrx_fire = 0 ! only wildfire currentPatch%rx_fire = 0 ! only rx fire currentPatch%rx_FI = 0.0_r8 - currentPatch%nonrx_FI = 0.0_r8 + currentPatch%nonrx_FI = 0.0_r8 if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag .eq. itrue) then @@ -456,7 +456,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) else ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on ! track wildfires greater than kW/m energy threshold if (currentPatch%FI > SF_val_fire_threshold) then - currentPatch%nonrx_fire = 1 + currentPatch%nonrx_fire = 1 end if end if @@ -560,7 +560,7 @@ subroutine CalculateRxfireAreaBurnt ( currentSite ) ! initialize site variables - currentSite%rxfire_area_final = 0.0_r8 + currentSite%rxfire_area_final = 0.0_r8 total_burnable_frac = 0.0_r8 ! update total burnable fraction @@ -716,7 +716,7 @@ subroutine crown_damage ( currentSite ) else ! Flames over top of canopy. - currentCohort%fraction_crown_burned = 1.0_r8 + currentCohort%fraction_crown_burned = 1.0_r8 endif endif @@ -815,7 +815,7 @@ subroutine post_fire_mortality ( currentSite ) if (currentPatch%fire == 1) then currentCohort => currentPatch%tallest do while(associated(currentCohort)) - currentCohort%fire_mort = 0.0_r8 + currentCohort%fire_mort = 0.0_r8 currentCohort%crownfire_mort = 0.0_r8 currentCohort%nonrx_fire_mort = 0.0_r8 currentCohort%nonrx_crown_mort = 0.0_r8 @@ -842,7 +842,7 @@ subroutine post_fire_mortality ( currentSite ) else currentCohort%rx_fire_mort = currentCohort%fire_mort currentCohort%rx_crown_mort = currentCohort%crownfire_mort - currentCohort%rx_cambial_mort = currentCohort%cambial_mort + currentCohort%rx_cambial_mort = currentCohort%cambial_mort end if currentCohort => currentCohort%shorter From 03bbe722a682ff3578d2811644104d16a8b1a2c8 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Mon, 31 Mar 2025 10:55:02 -0700 Subject: [PATCH 050/194] more white space cleaning --- main/FatesRestartInterfaceMod.F90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index 4f50378f6a..18dac80b8e 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -1732,7 +1732,7 @@ subroutine define_restart_vars(this, initialize_variables) long_name='disturbance rates by donor land-use type, receiver land-use type, and disturbance type', & units='1/day', initialize=initialize_variables,ivar=ivar, index = ir_disturbance_rates_siluludi) - if ( hlm_regeneration_model == TRS_regeneration ) then + if ( hlm_regeneration_model == TRS_regeneration ) then call this%DefineRMeanRestartVar(vname='fates_seedling_layer_par24',vtype=cohort_r8, & long_name='24-hour seedling layer PAR', & @@ -1752,9 +1752,8 @@ subroutine define_restart_vars(this, initialize_variables) call this%DefineRMeanRestartVar(vname='fates_sdlng_mdd',vtype=cohort_r8, & long_name='seedling moisture deficit days', & - units='mm days', initialize=initialize_variables,ivar=ivar, index = ir_sdlng_mdd_pa) - - end if + units='mm days', initialize=initialize_variables,ivar=ivar, index = ir_sdlng_mdd_pa) + end if call this%DefineRMeanRestartVar(vname='fates_tveglpapatch',vtype=cohort_r8, & long_name='running average (EMA) of patch veg temp for photo acclim', & From 942361365950dfd77e08a29a85864b060cab5ce0 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Thu, 6 Mar 2025 17:37:27 -0800 Subject: [PATCH 051/194] added grazing and burn fluxes as bc_out variables so that HLMs can calculate NBP --- biogeochem/EDPatchDynamicsMod.F90 | 36 ++++++++++++++++++++++--------- biogeochem/EDPhysiologyMod.F90 | 16 ++++++++------ main/EDMainMod.F90 | 4 ++-- main/FatesInterfaceMod.F90 | 6 +++++- main/FatesInterfaceTypesMod.F90 | 4 ++++ 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index b1fc9af66d..17992578e4 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -48,6 +48,7 @@ module EDPatchDynamicsMod use FatesConstantsMod , only : nocomp_bareground use FatesInterfaceTypesMod , only : hlm_use_planthydro use FatesInterfaceTypesMod , only : bc_in_type + use FatesInterfaceTypesMod , only : bc_out_type use FatesInterfaceTypesMod , only : numpft use FatesInterfaceTypesMod , only : hlm_stepsize use FatesInterfaceTypesMod , only : hlm_use_sp @@ -482,7 +483,7 @@ end subroutine disturbance_rates ! ============================================================================ - subroutine spawn_patches( currentSite, bc_in) + subroutine spawn_patches( currentSite, bc_in, bc_out) ! ! !DESCRIPTION: ! In this subroutine, the following happens, @@ -509,6 +510,7 @@ subroutine spawn_patches( currentSite, bc_in) ! !ARGUMENTS: type (ed_site_type), intent(inout) :: currentSite type (bc_in_type), intent(in) :: bc_in + type (bc_out_type), intent(in) :: bc_out ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: newPatch @@ -768,13 +770,13 @@ subroutine spawn_patches( currentSite, bc_in) end if case (dtype_ifire) call fire_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis,bc_in) + newPatch, patch_site_areadis,bc_in, bc_out) case (dtype_ifall) call mortality_litter_fluxes(currentSite, currentPatch, & newPatch, patch_site_areadis,bc_in) case (dtype_ilandusechange) call landusechange_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis,bc_in, & + newPatch, patch_site_areadis,bc_in, bc_out, & clearing_matrix(i_donorpatch_landuse_type,i_landusechange_receiverpatchlabel)) ! if land use change, then may need to change nocomp pft, so tag as having transitioned LU @@ -1073,6 +1075,8 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%flux_diags%elem(el)%burned_liveveg + & leaf_burn_frac * leaf_m * nc%n * area_inv + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & + leaf_burn_frac * leaf_m * nc%n end do ! Here the mass is removed from the plant @@ -1989,7 +1993,9 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass*retain_m2 site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + ! Transfer below ground CWD (none burns) do sl = 1,currentSite%nlevsoil @@ -2018,7 +2024,9 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt%leaf_fines(dcmpy) = curr_litt%leaf_fines(dcmpy) + donatable_mass*retain_m2 site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%root_fines(dcmpy,sl) * patch_site_areadis @@ -2068,7 +2076,7 @@ end subroutine TransLitterNewPatch ! ============================================================================ subroutine fire_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis, bc_in) + newPatch, patch_site_areadis, bc_in, bc_out) ! ! !DESCRIPTION: ! CWD pool burned by a fire. @@ -2088,6 +2096,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in + type(bc_out_type) , intent(in) :: bc_out ! ! !LOCAL VARIABLES: @@ -2229,8 +2238,8 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - - + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2292,6 +2301,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & burned_mass = num_dead_trees * SF_val_CWD_frac_adj(c) * bstem * & currentCohort%fraction_crown_burned site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass endif new_litt%ag_cwd(c) = new_litt%ag_cwd(c) + donatable_mass * donate_m2 curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass * retain_m2 @@ -2542,7 +2552,7 @@ end subroutine mortality_litter_fluxes ! ============================================================================ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis, bc_in, & + newPatch, patch_site_areadis, bc_in, bc_out, & clearing_matrix_element) ! ! !DESCRIPTION: @@ -2559,6 +2569,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in + type(bc_out_type) , intent(in) :: bc_out logical , intent(in) :: clearing_matrix_element ! whether or not to clear vegetation ! @@ -2702,7 +2713,9 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & end do site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2762,6 +2775,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & EDPftvarcon_inst%landusechange_frac_burned(pft) site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass else ! all other pools can end up as timber products or burn or go to litter donatable_mass = donatable_mass * (1.0_r8-EDPftvarcon_inst%landusechange_frac_exported(pft)) * & (1.0_r8-EDPftvarcon_inst%landusechange_frac_burned(pft)) @@ -2775,6 +2789,8 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + trunk_product_site = trunk_product_site + & woodproduct_mass diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 3b1d61e914..31510ea96b 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -429,7 +429,7 @@ end subroutine GenerateDamageAndLitterFluxes ! ============================================================================ - subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) + subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in, bc_out ) ! ----------------------------------------------------------------------------------- ! @@ -437,8 +437,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) ! associated with seed turnover, seed influx, litterfall from live and ! dead plants, germination, and fragmentation. ! - ! At this time we do not have explicit herbivory, and burning losses to litter - ! are handled elsewhere. + ! Herbivory is handled here. burning losses to litter are handled elsewhere. ! ! Note: The processes conducted here DO NOT handle litter fluxes associated ! with disturbance. Those fluxes are handled elsewhere (EDPatchDynamcisMod) @@ -452,6 +451,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) type(ed_site_type), intent(inout) :: currentSite type(fates_patch_type), intent(inout) :: currentPatch type(bc_in_type), intent(in) :: bc_in + type(bc_out_type), intent(in) :: bc_out ! ! !LOCAL VARIABLES: @@ -481,7 +481,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) ! Send fluxes from newly created litter into the litter pools ! This litter flux is from non-disturbance inducing mortality, as well ! as litter fluxes from live trees - call CWDInput(currentSite, currentPatch, litt,bc_in) + call CWDInput(currentSite, currentPatch, litt,bc_in, bc_out) ! Only calculate fragmentation flux over layers that are active ! (RGK-Mar2019) SHOULD WE MAX THIS AT 1? DONT HAVE TO @@ -2788,7 +2788,7 @@ end subroutine recruitment ! ====================================================================================== - subroutine CWDInput( currentSite, currentPatch, litt, bc_in) + subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) ! ! !DESCRIPTION: @@ -2808,6 +2808,7 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) type(fates_patch_type),intent(inout), target :: currentPatch type(litter_type),intent(inout),target :: litt type(bc_in_type),intent(in) :: bc_in + type(bc_out_type),intent(in) :: bc_out ! ! !LOCAL VARIABLES: @@ -2955,12 +2956,15 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) elflux_diags%root_litter_input(pft) + & (fnrt_m_turnover + store_m_turnover ) * currentCohort%n - ! send the part of the herbivory flux that doesn't go to litter to the atmosphere + ! send the part of the herbivory flux that doesn't go to litter to the atmosphere (and also for tracking) site_mass%herbivory_flux_out = & site_mass%herbivory_flux_out + & leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n + bc_out%grazing_closs_to_atm_si = bc_out%grazing_closs_to_atm_si + & + leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n + ! Assumption: turnover from deadwood and sapwood are lumped together in CWD pool !update partitioning of stem wood (struct + sapw) to cwd based on cohort dbh diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 979aa2960d..339615ec58 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -294,7 +294,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! make new patches from disturbed land if (do_patch_dynamics.eq.itrue ) then - call spawn_patches(currentSite, bc_in) + call spawn_patches(currentSite, bc_in, bc_out) call TotalBalanceCheck(currentSite,3) @@ -782,7 +782,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) call GenerateDamageAndLitterFluxes( currentSite, currentPatch, bc_in) - call PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in) + call PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in, bc_out) call PreDisturbanceIntegrateLitter(currentPatch ) diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index c731819d65..ee1ac8fc45 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -369,7 +369,11 @@ subroutine zero_bcs(fates,s) write(fates_log(), *) 'hlm_parteh_mode: ',hlm_parteh_mode call endrun(msg=errMsg(sourcefile, __LINE__)) end select - + + ! carbon loss to atmosphere pathways + fates%bc_out(s)%grazing_closs_to_atm_si(:) = 0.0_r8 + fates%bc_out(s)%fire_closs_to_atm_si(:) = 0.0_r8 + fates%bc_out(s)%rssun_pa(:) = 0.0_r8 fates%bc_out(s)%rssha_pa(:) = 0.0_r8 diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 416af2728a..b05babd441 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -813,6 +813,10 @@ module FatesInterfaceTypesMod real(r8) :: gpp_site ! Site level GPP, for NBP diagnosis in HLM [Site-Level, gC m-2 s-1] real(r8) :: ar_site ! Site level Autotrophic Resp, for NBP diagnosis in HLM [Site-Level, gC m-2 s-1] + ! direct carbon loss to atm pathways + real(r8) :: grazing_closs_to_atm_si ! Loss of carbon to atmosphere via grazing [Site-Level, gC m-2 s-1] + real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] + end type bc_out_type From 1517420292ca0298eb54fabad5e8d049aa45017a Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Fri, 7 Mar 2025 15:45:18 -0800 Subject: [PATCH 052/194] fixes on new bc_out vars --- biogeochem/EDPatchDynamicsMod.F90 | 38 ++++++++++++++++--------------- biogeochem/EDPhysiologyMod.F90 | 7 +++--- main/FatesInterfaceMod.F90 | 4 ++-- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 17992578e4..65b5f8def7 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -510,7 +510,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! !ARGUMENTS: type (ed_site_type), intent(inout) :: currentSite type (bc_in_type), intent(in) :: bc_in - type (bc_out_type), intent(in) :: bc_out + type (bc_out_type), intent(inout) :: bc_out ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: newPatch @@ -755,7 +755,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) call CopyPatchMeansTimers(currentPatch, newPatch) - call TransLitterNewPatch( currentSite, currentPatch, newPatch, patch_site_areadis, i_disturbance_type) + call TransLitterNewPatch( currentSite, currentPatch, newPatch, patch_site_areadis, i_disturbance_type, bc_out) ! Transfer in litter fluxes from plants in various contexts of death and destruction select case(i_disturbance_type) @@ -1076,7 +1076,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) leaf_burn_frac * leaf_m * nc%n * area_inv bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & - leaf_burn_frac * leaf_m * nc%n + leaf_burn_frac * leaf_m * nc%n * ha_per_m2 * days_per_sec end do ! Here the mass is removed from the plant @@ -1425,7 +1425,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) allocate(temp_patch) - call split_patch(currentSite, currentPatch, temp_patch, fraction_to_keep, newp_area) + call split_patch(currentSite, currentPatch, temp_patch, fraction_to_keep, newp_area, bc_out) ! temp_patch%nocomp_pft_label = 0 @@ -1528,7 +1528,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! split buffer patch in two, keeping the smaller buffer patch to put into new patches allocate(temp_patch) - call split_patch(currentSite, buffer_patch, temp_patch, fraction_to_keep, newp_area) + call split_patch(currentSite, buffer_patch, temp_patch, fraction_to_keep, newp_area, bc_out) ! give the new patch the intended nocomp PFT label temp_patch%nocomp_pft_label = i_pft @@ -1637,7 +1637,7 @@ end subroutine spawn_patches ! ----------------------------------------------------------------------------------------- - subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, area_to_remove) + subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, area_to_remove, bc_out) ! ! !DESCRIPTION: ! Split a patch into two patches that are identical except in their areas @@ -1648,6 +1648,7 @@ subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, a type(fates_patch_type) , intent(inout), pointer :: new_patch ! New Patch real(r8), intent(in) :: fraction_to_keep ! fraction of currentPatch to keep, the rest goes to newpatch real(r8), intent(in), optional :: area_to_remove ! area of currentPatch to remove, the rest goes to newpatch + type(bc_out_type) , intent(inout) :: bc_out ! ! !LOCAL VARIABLES: integer :: el ! element loop index @@ -1684,7 +1685,7 @@ subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, a call CopyPatchMeansTimers(currentPatch, new_patch) - call TransLitterNewPatch( currentSite, currentPatch, new_patch, temp_area, 0) + call TransLitterNewPatch( currentSite, currentPatch, new_patch, temp_area, 0, bc_out) ! Next, we loop through the cohorts in the donor patch, copy them with ! area modified number density into the new-patch, and apply survivorship. @@ -1819,7 +1820,8 @@ subroutine TransLitterNewPatch(currentSite, & currentPatch, & newPatch, & patch_site_areadis, & - dist_type) + dist_type, & + bc_out) ! ----------------------------------------------------------------------------------- ! @@ -1869,7 +1871,7 @@ subroutine TransLitterNewPatch(currentSite, & real(r8) , intent(in) :: patch_site_areadis ! Area being donated ! by current patch integer, intent(in) :: dist_type ! disturbance type - + type(bc_out_type) , intent(inout) :: bc_out ! locals type(site_massbal_type), pointer :: site_mass @@ -1994,7 +1996,7 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec ! Transfer below ground CWD (none burns) @@ -2025,7 +2027,7 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil @@ -2096,7 +2098,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in - type(bc_out_type) , intent(in) :: bc_out + type(bc_out_type) , intent(inout) :: bc_out ! ! !LOCAL VARIABLES: @@ -2238,7 +2240,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2301,7 +2303,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & burned_mass = num_dead_trees * SF_val_CWD_frac_adj(c) * bstem * & currentCohort%fraction_crown_burned site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec endif new_litt%ag_cwd(c) = new_litt%ag_cwd(c) + donatable_mass * donate_m2 curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass * retain_m2 @@ -2569,7 +2571,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in - type(bc_out_type) , intent(in) :: bc_out + type(bc_out_type) , intent(inout) :: bc_out logical , intent(in) :: clearing_matrix_element ! whether or not to clear vegetation ! @@ -2714,7 +2716,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2775,7 +2777,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & EDPftvarcon_inst%landusechange_frac_burned(pft) site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec else ! all other pools can end up as timber products or burn or go to litter donatable_mass = donatable_mass * (1.0_r8-EDPftvarcon_inst%landusechange_frac_exported(pft)) * & (1.0_r8-EDPftvarcon_inst%landusechange_frac_burned(pft)) @@ -2789,7 +2791,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec trunk_product_site = trunk_product_site + & woodproduct_mass diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 31510ea96b..664669ea48 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -451,7 +451,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in, bc_out type(ed_site_type), intent(inout) :: currentSite type(fates_patch_type), intent(inout) :: currentPatch type(bc_in_type), intent(in) :: bc_in - type(bc_out_type), intent(in) :: bc_out + type(bc_out_type), intent(inout) :: bc_out ! ! !LOCAL VARIABLES: @@ -2808,7 +2808,7 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) type(fates_patch_type),intent(inout), target :: currentPatch type(litter_type),intent(inout),target :: litt type(bc_in_type),intent(in) :: bc_in - type(bc_out_type),intent(in) :: bc_out + type(bc_out_type),intent(inout) :: bc_out ! ! !LOCAL VARIABLES: @@ -2963,7 +2963,8 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n bc_out%grazing_closs_to_atm_si = bc_out%grazing_closs_to_atm_si + & - leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n + leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n * & + ha_per_m2 * days_per_sec ! Assumption: turnover from deadwood and sapwood are lumped together in CWD pool diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index ee1ac8fc45..0894b58768 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -371,8 +371,8 @@ subroutine zero_bcs(fates,s) end select ! carbon loss to atmosphere pathways - fates%bc_out(s)%grazing_closs_to_atm_si(:) = 0.0_r8 - fates%bc_out(s)%fire_closs_to_atm_si(:) = 0.0_r8 + fates%bc_out(s)%grazing_closs_to_atm_si = 0.0_r8 + fates%bc_out(s)%fire_closs_to_atm_si = 0.0_r8 fates%bc_out(s)%rssun_pa(:) = 0.0_r8 fates%bc_out(s)%rssha_pa(:) = 0.0_r8 From fe2427339487ccae4ae2b9da4b89a61303b9a7d2 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Thu, 20 Mar 2025 12:23:59 -0700 Subject: [PATCH 053/194] added carbon stock vars to bc_out data structure --- main/EDMainMod.F90 | 5 +++++ main/FatesInterfaceMod.F90 | 4 ++++ main/FatesInterfaceTypesMod.F90 | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 339615ec58..c94852b6df 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -837,6 +837,7 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch + real(r8) :: total_stock ! dummy variable for receiving from sitemassstock !----------------------------------------------------------------------- ! check patch order (set second argument to true) @@ -903,6 +904,10 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) endif endif + ! report summary diagnostic values of FATES carbon mass pools for HLM to include in total land stocks + call SiteMassStock(currentSite,carbon12_element,total_stock,& + bc_out%veg_c_si, bc_out%litter_cwd_c_si, bc_out%seed_c_si) + end subroutine ed_update_site !-------------------------------------------------------------------------------! diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 0894b58768..4bc525e8d5 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -416,6 +416,10 @@ subroutine zero_bcs(fates,s) fates%bc_in(s)%hlm_luh_transitions(:) = 0.0_r8 end if + fates%bc_out(s)%veg_c_si = 0.0_r8 + fates%bc_out(s)%litter_cwd_c_si = 0.0_r8 + fates%bc_out(s)%seed_c_si = 0.0_r8 + return end subroutine zero_bcs diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index b05babd441..46bd25b657 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -817,6 +817,11 @@ module FatesInterfaceTypesMod real(r8) :: grazing_closs_to_atm_si ! Loss of carbon to atmosphere via grazing [Site-Level, gC m-2 s-1] real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] + ! summary carbon stock variables + real(r8) :: veg_c_si ! Total vegetation carbon [Site-Level, kgC m-2] + real(r8) :: litter_cwd_c_si ! Total litter plus CWD carbon [Site-Level, kgC m-2] + real(r8) :: seed_c_si ! Total seed carbon [Site-Level, kgC m-2] + end type bc_out_type From ecf2afe5ff9ea0cd4d528d01433188a03fe17ae2 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Fri, 4 Apr 2025 14:03:43 -0700 Subject: [PATCH 054/194] unit fixes etc on NBP and TOTECOSYSC calcs --- biogeochem/EDPhysiologyMod.F90 | 15 +++++++++++++++ main/EDMainMod.F90 | 11 +++++++++++ main/FatesInterfaceTypesMod.F90 | 6 +++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 664669ea48..cc62825072 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -155,6 +155,7 @@ module EDPhysiologyMod public :: calculate_SP_properties public :: recruitment public :: ZeroLitterFluxes + public :: ZeroBCOutFluxes public :: ZeroAllocationRates public :: PreDisturbanceLitterFluxes @@ -231,6 +232,20 @@ end subroutine ZeroLitterFluxes ! ===================================================================================== + subroutine ZeroBCOutFluxes (bc_out) + + ! !ARGUMENTS + type(bc_out_type), intent(inout) :: bc_out + + bc_out%grazing_closs_to_atm_si = 0._r8 + bc_out%fire_closs_to_atm_si = 0._r8 + bc_out%gpp_site = 0._r8 + bc_out%ar_site = 0._r8 + + end subroutine ZeroBCOutFluxes + + ! ===================================================================================== + subroutine ZeroAllocationRates( currentSite ) ! !ARGUMENTS diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index c94852b6df..929dbf44e9 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -46,6 +46,7 @@ module EDMainMod use EDPhysiologyMod , only : SeedUpdate use EDPhysiologyMod , only : ZeroAllocationRates use EDPhysiologyMod , only : ZeroLitterFluxes + use EDPhysiologyMod , only : ZeroBCOutFluxes use EDPhysiologyMod , only : PreDisturbanceLitterFluxes use EDPhysiologyMod , only : PreDisturbanceIntegrateLitter use EDPhysiologyMod , only : UpdateRecruitL2FR @@ -78,6 +79,7 @@ module EDMainMod use FatesConstantsMod , only : nearzero use FatesConstantsMod , only : m2_per_ha use FatesConstantsMod , only : sec_per_day + use FatesConstantsMod , only : g_per_kg use FatesConstantsMod , only : nocomp_bareground use FatesPlantHydraulicsMod , only : do_growthrecruiteffects use FatesPlantHydraulicsMod , only : UpdateSizeDepPlantHydProps @@ -189,6 +191,9 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! Zero fluxes in and out of litter pools call ZeroLitterFluxes(currentSite) + ! Zero diagnostic bc_out fluxes + call ZeroBCOutFluxes(bc_out) + ! Zero mass balance call TotalBalanceCheck(currentSite, 0) @@ -908,6 +913,12 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) call SiteMassStock(currentSite,carbon12_element,total_stock,& bc_out%veg_c_si, bc_out%litter_cwd_c_si, bc_out%seed_c_si) + ! because the outputs of SiteMassStock are in kg C/ha, convert units to g C/m2 + bc_out%veg_c_si = bc_out%veg_c_si * g_per_kg * AREA_INV + bc_out%litter_cwd_c_si = bc_out%litter_cwd_c_si * g_per_kg * AREA_INV + bc_out%seed_c_si = bc_out%seed_c_si * g_per_kg * AREA_INV + + end subroutine ed_update_site !-------------------------------------------------------------------------------! diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 46bd25b657..67323a40d4 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -818,9 +818,9 @@ module FatesInterfaceTypesMod real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] ! summary carbon stock variables - real(r8) :: veg_c_si ! Total vegetation carbon [Site-Level, kgC m-2] - real(r8) :: litter_cwd_c_si ! Total litter plus CWD carbon [Site-Level, kgC m-2] - real(r8) :: seed_c_si ! Total seed carbon [Site-Level, kgC m-2] + real(r8) :: veg_c_si ! Total vegetation carbon [Site-Level, gC m-2] + real(r8) :: litter_cwd_c_si ! Total litter plus CWD carbon [Site-Level, gC m-2] + real(r8) :: seed_c_si ! Total seed carbon [Site-Level, gC m-2] end type bc_out_type From b73443f36dc0d5da1bc0d1f3320dbf6338d6cf8c Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 24 Apr 2025 11:50:29 -0400 Subject: [PATCH 055/194] Fixes to rad_error to accomodate zenith and do-albedo consistency --- biogeochem/EDPatchDynamicsMod.F90 | 4 - biogeochem/FatesPatchMod.F90 | 17 +- main/FatesHistoryInterfaceMod.F90 | 313 ++++++++++++++------------- main/FatesInterfaceMod.F90 | 12 +- radiation/FatesNormanRadMod.F90 | 6 +- radiation/FatesRadiationDriveMod.F90 | 60 ++--- 6 files changed, 203 insertions(+), 209 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index b1fc9af66d..b37b080ea4 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -3213,10 +3213,6 @@ subroutine fuse_2_patches(csite, dp, rp) rp%c_stomata = (dp%c_stomata*dp%area + rp%c_stomata*rp%area) * inv_sum_area rp%c_lblayer = (dp%c_lblayer*dp%area + rp%c_lblayer*rp%area) * inv_sum_area - ! Radiation - rp%rad_error(1) = (dp%rad_error(1)*dp%area + rp%rad_error(1)*rp%area) * inv_sum_area - rp%rad_error(2) = (dp%rad_error(2)*dp%area + rp%rad_error(2)*rp%area) * inv_sum_area - rp%area = rp%area + dp%area !THIS MUST COME AT THE END! !insert donor cohorts into recipient patch diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 9b3b9ef919..a0b6b3ac1a 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -128,8 +128,8 @@ module FatesPatchMod real(r8) :: c_stomata ! mean stomatal conductance of all leaves in the patch [umol/m2/s] real(r8) :: c_lblayer ! mean boundary layer conductance of all leaves in the patch [umol/m2/s] - real(r8),allocatable :: nrmlzd_parprof_pft_dir_z(:,:,:,:) !num_rad_stream_types,nclmax,maxpft,nlevleaf) - real(r8),allocatable :: nrmlzd_parprof_pft_dif_z(:,:,:,:) !num_rad_stream_types,nclmax,maxpft,nlevleaf) + real(r8),allocatable :: nrmlzd_parprof_pft_dir_z(:,:,:) ! nclmax,maxpft,nlevleaf) + real(r8),allocatable :: nrmlzd_parprof_pft_dif_z(:,:,:) ! nclmax,maxpft,nlevleaf) !--------------------------------------------------------------------------- @@ -369,8 +369,8 @@ subroutine ReAllocateDynamics(this) allocate(this%fabd_sha_z(ncan,numpft,nveg)) allocate(this%fabi_sun_z(ncan,numpft,nveg)) allocate(this%fabi_sha_z(ncan,numpft,nveg)) - allocate(this%nrmlzd_parprof_pft_dir_z(num_rad_stream_types,ncan,numpft,nveg)) - allocate(this%nrmlzd_parprof_pft_dif_z(num_rad_stream_types,ncan,numpft,nveg)) + allocate(this%nrmlzd_parprof_pft_dir_z(ncan,numpft,nveg)) + allocate(this%nrmlzd_parprof_pft_dif_z(ncan,numpft,nveg)) allocate(this%ed_parsun_z(ncan,numpft,nveg)) allocate(this%ed_parsha_z(ncan,numpft,nveg)) allocate(this%ed_laisun_z(ncan,numpft,nveg)) @@ -393,8 +393,8 @@ subroutine NanDynamics(this) this%tlai_profile(:,:,:) = nan this%tsai_profile(:,:,:) = nan this%canopy_area_profile(:,:,:) = nan - this%nrmlzd_parprof_pft_dir_z(:,:,:,:) = nan - this%nrmlzd_parprof_pft_dif_z(:,:,:,:) = nan + this%nrmlzd_parprof_pft_dir_z(:,:,:) = nan + this%nrmlzd_parprof_pft_dif_z(:,:,:) = nan this%fabd_sun_z(:,:,:) = nan this%fabd_sha_z(:,:,:) = nan @@ -521,8 +521,8 @@ subroutine ZeroDynamics(this) this%fabi_sun_z(:,:,:) = 0._r8 this%fabd_sha_z(:,:,:) = 0._r8 this%fabi_sha_z(:,:,:) = 0._r8 - this%nrmlzd_parprof_pft_dir_z(:,:,:,:) = 0._r8 - this%nrmlzd_parprof_pft_dif_z(:,:,:,:) = 0._r8 + this%nrmlzd_parprof_pft_dir_z(:,:,:) = 0._r8 + this%nrmlzd_parprof_pft_dif_z(:,:,:) = 0._r8 ! Added this%elai_profile(:,:,:) = 0._r8 @@ -562,7 +562,6 @@ subroutine ZeroValues(this) this%c_lblayer = 0.0_r8 ! RADIATION - this%rad_error(:) = 0.0_r8 this%tr_soil_dir_dif(:) = 0.0_r8 this%fab(:) = 0.0_r8 this%fabi(:) = 0.0_r8 diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 454e89c899..e3f6ffa668 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -4962,14 +4962,13 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) sum_area_rad = sum(age_area_rad(:)) - if_anyrad: if(sum_area_rad sites(s)%oldest_patch do while(associated(cpatch)) if( abs(cpatch%rad_error(ivis))>nearzero ) then @@ -4979,7 +4978,7 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) cpatch%rad_error(ivis)*cpatch%total_canopy_area/sum_area_rad hio_nir_rad_err_si(io_si) = hio_nir_rad_err_si(io_si) + & cpatch%rad_error(inir)*cpatch%total_canopy_area/sum_area_rad - + end if cpatch => cpatch%younger end do @@ -5125,7 +5124,8 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) real(r8) :: clllpf_area ! area footprint (m2) for the current cl x ll x pft bin real(r8) :: clll_area ! area footprint (m2) for the cl x ll bin (ie adds up pfts in parallel) real(r8) :: cl_area ! total weight of all ll x pft bins in the canopy layer - + real(r8) :: parprof_pft_dir_z,parprof_pft_dif_z ! PAR intensity for dir/diff for pft/canopy/leaf layer (w/m2) + type(fates_patch_type),pointer :: cpatch type(fates_cohort_type),pointer :: ccohort real(r8) :: dt_tstep_inv ! Time step in frequency units (/s) @@ -5302,8 +5302,8 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) endif end associate endif - -!!! canopy leaf carbon balance + + ! canopy leaf carbon balance ican = ccohort%canopy_layer do ileaf=1,ccohort%nv cnlf_indx = ileaf + (ican-1) * nlevleaf @@ -5315,88 +5315,96 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) enddo ! cohort loop - ! summarize radiation profiles through the canopy - ! -------------------------------------------------------------------- + ! Radiation diagnostics + ! Only process diagnostics if the sun is out + if_zenith1: if( sites(s)%coszen>0._r8 ) then - do_pft1: do ipft=1,numpft - do_canlev1: do ican=1,cpatch%ncl_p - do_leaflev1: do ileaf=1,cpatch%nleaf(ican,ipft) + do_pft1: do ipft=1,numpft + do_canlev1: do ican=1,cpatch%ncl_p + do_leaflev1: do ileaf=1,cpatch%nrad(ican,ipft) - ! calculate where we are on multiplexed dimensions - clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax - cnlf_indx = ileaf + (ican-1) * nlevleaf + ! calculate where we are on multiplexed dimensions + clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax + cnlf_indx = ileaf + (ican-1) * nlevleaf - ! canopy_area_profile is the fraction of the total canopy area that - ! is occupied by this bin. If you add up the top leaf layer bins in the - ! top canopy layers, for all pfts, that should equal to 1 + ! canopy_area_profile is the fraction of the total canopy area that + ! is occupied by this bin. If you add up the top leaf layer bins in the + ! top canopy layers, for all pfts, that should equal to 1 - clllpf_area = cpatch%canopy_area_profile(ican,ipft,ileaf)*cpatch%total_canopy_area + clllpf_area = cpatch%canopy_area_profile(ican,ipft,ileaf)*cpatch%total_canopy_area - ! Canopy by leaf by pft level diagnostics - ! ------------------------------------------------------------------- - hio_parsun_z_si_cnlfpft(io_si,clllpf_indx) = hio_parsun_z_si_cnlfpft(io_si,clllpf_indx) + & - cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area + ! Canopy by leaf by pft level diagnostics + ! ------------------------------------------------------------------- + hio_parsun_z_si_cnlfpft(io_si,clllpf_indx) = hio_parsun_z_si_cnlfpft(io_si,clllpf_indx) + & + cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area - hio_parsha_z_si_cnlfpft(io_si,clllpf_indx) = hio_parsha_z_si_cnlfpft(io_si,clllpf_indx) + & - cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area + hio_parsha_z_si_cnlfpft(io_si,clllpf_indx) = hio_parsha_z_si_cnlfpft(io_si,clllpf_indx) + & + cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area - ! elai_profile is the m2 of leaf inside the m2 of bin. + ! elai_profile is the m2 of leaf inside the m2 of bin. - hio_laisun_clllpf(io_si, clllpf_indx) = hio_laisun_clllpf(io_si, clllpf_indx) + & - cpatch%elai_profile(ican,ipft,ileaf)*cpatch%f_sun(ican,ipft,ileaf)*clllpf_area + hio_laisun_clllpf(io_si, clllpf_indx) = hio_laisun_clllpf(io_si, clllpf_indx) + & + cpatch%elai_profile(ican,ipft,ileaf)*cpatch%f_sun(ican,ipft,ileaf)*clllpf_area - hio_laisha_clllpf(io_si,clllpf_indx) = hio_laisha_clllpf(io_si,clllpf_indx) + & - cpatch%elai_profile(ican,ipft,ileaf)*(1._r8-cpatch%f_sun(ican,ipft,ileaf))*clllpf_area + hio_laisha_clllpf(io_si,clllpf_indx) = hio_laisha_clllpf(io_si,clllpf_indx) + & + cpatch%elai_profile(ican,ipft,ileaf)*(1._r8-cpatch%f_sun(ican,ipft,ileaf))*clllpf_area - hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) + & - cpatch%parprof_pft_dir_z(ican,ipft,ileaf) * clllpf_area + parprof_pft_dir_z = bc_in(s)%solad_parb(ifp,ipar) * & + cpatch%nrmlzd_parprof_pft_dir_z(ican,ipft,ileaf) - hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) + & - cpatch%parprof_pft_dif_z(ican,ipft,ileaf) * clllpf_area + parprof_pft_dif_z = bc_in(s)%solai_parb(ifp,ipar) * & + cpatch%nrmlzd_parprof_pft_dif_z(ican,ipft,ileaf) - ! The fractional area of Canopy layer and PFTs can be used - ! do upscale the CLLLPF properties - hio_crownfrac_clllpf(io_si,clllpf_indx) = hio_crownfrac_clllpf(io_si,clllpf_indx) + & - clllpf_area + hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) + & + parprof_pft_dir_z * clllpf_area + hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) + & + parprof_pft_dif_z * clllpf_area - ! Canopy by leaf layer (mean across pfts) level diagnostics - ! ---------------------------------------------------------------------------- - hio_parprof_dir_si_cnlf(io_si,cnlf_indx) = hio_parprof_dir_si_cnlf(io_si,cnlf_indx) + & - cpatch%parprof_pft_dir_z(ican,ipft,ileaf) * clllpf_area + ! The fractional area of Canopy layer and PFTs can be used + ! do upscale the CLLLPF properties + hio_crownfrac_clllpf(io_si,clllpf_indx) = hio_crownfrac_clllpf(io_si,clllpf_indx) + & + clllpf_area + + + ! Canopy by leaf layer (mean across pfts) level diagnostics + ! ---------------------------------------------------------------------------- + hio_parprof_dir_si_cnlf(io_si,cnlf_indx) = hio_parprof_dir_si_cnlf(io_si,cnlf_indx) + & + parprof_pft_dir_z * clllpf_area - hio_parprof_dif_si_cnlf(io_si,cnlf_indx) = hio_parprof_dif_si_cnlf(io_si,cnlf_indx) + & - cpatch%parprof_pft_dif_z(ican,ipft,ileaf) * clllpf_area + hio_parprof_dif_si_cnlf(io_si,cnlf_indx) = hio_parprof_dif_si_cnlf(io_si,cnlf_indx) + & + parprof_pft_dif_z * clllpf_area - hio_parsun_z_si_cnlf(io_si,cnlf_indx) = hio_parsun_z_si_cnlf(io_si,cnlf_indx) + & - cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area + hio_parsun_z_si_cnlf(io_si,cnlf_indx) = hio_parsun_z_si_cnlf(io_si,cnlf_indx) + & + cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area - hio_parsha_z_si_cnlf(io_si,cnlf_indx) = hio_parsha_z_si_cnlf(io_si,cnlf_indx) + & - cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area + hio_parsha_z_si_cnlf(io_si,cnlf_indx) = hio_parsha_z_si_cnlf(io_si,cnlf_indx) + & + cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area - hio_laisun_z_si_cnlf(io_si,cnlf_indx) = hio_laisun_z_si_cnlf(io_si,cnlf_indx) + & - cpatch%f_sun(ican,ipft,ileaf)*clllpf_area + hio_laisun_z_si_cnlf(io_si,cnlf_indx) = hio_laisun_z_si_cnlf(io_si,cnlf_indx) + & + cpatch%f_sun(ican,ipft,ileaf)*clllpf_area - hio_laisha_z_si_cnlf(io_si,cnlf_indx) = hio_laisha_z_si_cnlf(io_si,cnlf_indx) + & - (1._r8-cpatch%f_sun(ican,ipft,ileaf))*clllpf_area + hio_laisha_z_si_cnlf(io_si,cnlf_indx) = hio_laisha_z_si_cnlf(io_si,cnlf_indx) + & + (1._r8-cpatch%f_sun(ican,ipft,ileaf))*clllpf_area - ! Canopy mean diagnostics - ! -------------------------------------------------------------- + ! Canopy mean diagnostics + ! -------------------------------------------------------------- - hio_parsun_si_can(io_si,ican) = hio_parsun_si_can(io_si,ican) + & - cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area - hio_parsha_si_can(io_si,ican) = hio_parsha_si_can(io_si,ican) + & - cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area + hio_parsun_si_can(io_si,ican) = hio_parsun_si_can(io_si,ican) + & + cpatch%ed_parsun_z(ican,ipft,ileaf) * clllpf_area + hio_parsha_si_can(io_si,ican) = hio_parsha_si_can(io_si,ican) + & + cpatch%ed_parsha_z(ican,ipft,ileaf) * clllpf_area - hio_laisun_si_can(io_si,ican) = hio_laisun_si_can(io_si,ican) + & - cpatch%f_sun(ican,ipft,ileaf)*cpatch%elai_profile(ican,ipft,ileaf) * clllpf_area - hio_laisha_si_can(io_si,ican) = hio_laisha_si_can(io_si,ican) + & - (1._r8-cpatch%f_sun(ican,ipft,ileaf))*cpatch%elai_profile(ican,ipft,ileaf) * clllpf_area + hio_laisun_si_can(io_si,ican) = hio_laisun_si_can(io_si,ican) + & + cpatch%f_sun(ican,ipft,ileaf)*cpatch%elai_profile(ican,ipft,ileaf) * clllpf_area + hio_laisha_si_can(io_si,ican) = hio_laisha_si_can(io_si,ican) + & + (1._r8-cpatch%f_sun(ican,ipft,ileaf))*cpatch%elai_profile(ican,ipft,ileaf) * clllpf_area - end do do_leaflev1 - end do do_canlev1 - end do do_pft1 + end do do_leaflev1 + end do do_canlev1 + end do do_pft1 + end if if_zenith1 cpatch => cpatch%younger end do !patch loop @@ -5404,97 +5412,98 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) ! Normalize the radiation multiplexed diagnostics ! Set values that dont have canopy elements to ignore ! ---------------------------------------------------------------------------- - - do_ican2: do ican = 1,nclmax - - cl_area = 0._r8 - do_ileaf2: do ileaf = 1,nlevleaf - - clll_area = 0._r8 - do_ipft2: do ipft = 1,numpft - - clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax - if( hio_crownfrac_clllpf(io_si,clllpf_indx)0._r8 ) then + do_ican2: do ican = 1,nclmax + + cl_area = 0._r8 + do_ileaf2: do ileaf = 1,nlevleaf + + clll_area = 0._r8 + do_ipft2: do ipft = 1,numpft + + clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax + if( hio_crownfrac_clllpf(io_si,clllpf_indx)0._r8 )then select case(hlm_radiation_model) @@ -189,8 +191,26 @@ subroutine FatesNormalizedCanopyRadiation(sites, bc_in, bc_out ) call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if - end do + + ! Fill in the diagnostic arrays for normalized radiation profiles + do_cl: do cl = 1,twostr%n_lyr + do_icol: do icol = 1,twostr%n_col(cl) + ft = twostr%scelg(cl,icol)%pft + nv = minloc(dlower_vai, DIM=1, MASK=(dlower_vai>vai)) + area_frac = twostr%scelg(cl,icol)%area + ! WAIT FOR THE BIN INDEXING PR TO GO IN ... + do iv = 1, nv + vai_top = dlower_vai(iv) + cpatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) = cpatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) + & + area_frac*twostr%GetRb(cl,icol,ivis,vai_top) + cpatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) = cpatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) + & + area_frac*twostr%GetRdDn(cl,icol,ivis,vai_top) + & + area_frac*twostr%GetRdUp(cl,icol,ivis,vai_top) + end do + end do do_icol + end do do_cl + end associate end select endif if_zenith_flag @@ -251,8 +271,6 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) cpatch%ed_parsha_z(:,:,:) = 0._r8 cpatch%ed_laisun_z(:,:,:) = 0._r8 cpatch%ed_laisha_z(:,:,:) = 0._r8 - cpatch%parprof_pft_dir_z(:,:,:) = 0._r8 - cpatch%parprof_pft_dif_z(:,:,:) = 0._r8 if_norm_twostr: if (hlm_radiation_model.eq.norman_solver) then @@ -317,29 +335,7 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) end do !ft end do !cl - ! Convert normalized radiation error units from fraction of radiation to W/m2 - do ib = 1,num_swb - cpatch%rad_error(ib) = cpatch%rad_error(ib) * & - (bc_in(s)%solad_parb(ifp,ib) + bc_in(s)%solai_parb(ifp,ib)) - end do - ! output the actual PAR profiles through the canopy for diagnostic purposes - do cl = 1, cpatch%ncl_p - do ft = 1,numpft - do iv = 1, cpatch%nrad(cl,ft) - cpatch%parprof_pft_dir_z(cl,ft,iv) = (bc_in(s)%solad_parb(ifp,ipar) * & - cpatch%nrmlzd_parprof_pft_dir_z(idirect,cl,ft,iv)) + & - (bc_in(s)%solai_parb(ifp,ipar) * & - cpatch%nrmlzd_parprof_pft_dir_z(idiffuse,cl,ft,iv)) - - cpatch%parprof_pft_dif_z(cl,ft,iv) = (bc_in(s)%solad_parb(ifp,ipar) * & - cpatch%nrmlzd_parprof_pft_dif_z(idirect,cl,ft,iv)) + & - (bc_in(s)%solai_parb(ifp,ipar) * & - cpatch%nrmlzd_parprof_pft_dif_z(idiffuse,cl,ft,iv)) - - end do ! iv - end do ! ft - end do ! cl else ! if_norm_twostr @@ -383,12 +379,6 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) vai_top = dlower_vai(iv)-dinc_vai(iv) vai_bot = min(dlower_vai(iv),twostr%scelg(cl,icol)%sai+twostr%scelg(cl,icol)%lai) - cpatch%parprof_pft_dir_z(cl,ft,iv) = cpatch%parprof_pft_dir_z(cl,ft,iv) + & - area_frac*twostr%GetRb(cl,icol,ivis,vai_top) - cpatch%parprof_pft_dif_z(cl,ft,iv) = cpatch%parprof_pft_dif_z(cl,ft,iv) + & - area_frac*twostr%GetRdDn(cl,icol,ivis,vai_top) + & - area_frac*twostr%GetRdUp(cl,icol,ivis,vai_top) - call twostr%GetAbsRad(cl,icol,ipar,vai_top,vai_bot, & Rb_abs,Rd_abs,Rd_abs_leaf,Rb_abs_leaf,R_abs_stem,R_abs_snow,leaf_sun_frac,call_fail) @@ -418,10 +408,6 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) do ft = 1,numpft do_iv: do iv = 1,cpatch%nleaf(cl,ft) if(area_vlpfcl(iv,ft,cl) Date: Tue, 6 May 2025 12:04:46 -0400 Subject: [PATCH 056/194] initializing radiation error diagnostics as ignore instead of zero. --- biogeochem/FatesPatchMod.F90 | 1 + main/FatesHistoryInterfaceMod.F90 | 12 ++++++++---- main/FatesRestartInterfaceMod.F90 | 3 ++- radiation/FatesRadiationDriveMod.F90 | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index a0b6b3ac1a..0334dfc989 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -568,6 +568,7 @@ subroutine ZeroValues(this) this%fabd(:) = 0.0_r8 this%sabs_dir(:) = 0.0_r8 this%sabs_dif(:) = 0.0_r8 + this%rad_error(:) = hlm_hio_ignore_value ! ROOTS this%btran_ft(:) = 0.0_r8 diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 436b735a34..8658adfc52 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -4978,6 +4978,11 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) ! We do not call the radiation solver if ! a) there is no vegetation ! b) there is no light! (ie cos(zenith) ~= 0) + ! c) the "do albedo" flag is true...but, this + ! may be false in coupled runs on alternate time-steps + ! and it is ok to carry over the previous errors + ! and diagnostics between these steps + age_area_rad(:) = 0._r8 cpatch => sites(s)%oldest_patch do while(associated(cpatch)) @@ -4986,7 +4991,7 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) ! solver was called. The solver will be called for NIR ! if VIS is called, and likewise the same for conservation ! error. So the check on VIS solve error will catch all. - if( abs(cpatch%rad_error(ivis))>nearzero ) then + if( abs(cpatch%rad_error(ivis)-hlm_hio_ignore_val)>nearzero ) then age_class = get_age_class_index(cpatch%age) age_area_rad(age_class) = age_area_rad(age_class) + cpatch%total_canopy_area end if @@ -4995,7 +5000,7 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) sum_area_rad = sum(age_area_rad(:)) - if_anyrad: if(sum_area_rad sites(s)%oldest_patch do while(associated(cpatch)) - if( abs(cpatch%rad_error(ivis))>nearzero ) then - age_class = get_age_class_index(cpatch%age) + if( abs(cpatch%rad_error(ivis)-hlm_hio_ignore_val)>nearzero ) then hio_vis_rad_err_si(io_si) = hio_vis_rad_err_si(io_si) + & cpatch%rad_error(ivis)*cpatch%total_canopy_area/sum_area_rad diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index f2c78051a1..9a6c9fcde1 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -29,6 +29,7 @@ module FatesRestartInterfaceMod use FatesInterfaceTypesMod, only : hlm_use_potentialveg use FatesInterfaceTypesMod, only : fates_maxElementsPerSite use FatesInterfaceTypesMod, only : hlm_use_tree_damage + use FatesInterfaceTypesMod, only : hlm_hio_ignore_val use FatesHydraulicsMemMod, only : nshell use FatesHydraulicsMemMod, only : n_hypool_ag use FatesHydraulicsMemMod, only : n_hypool_troot @@ -3875,7 +3876,7 @@ subroutine update_3dpatch_radiation(this, nsites, sites, bc_out) ! zero diagnostic radiation profiles currentPatch%nrmlzd_parprof_pft_dir_z(:,:,:,:) = 0._r8 currentPatch%nrmlzd_parprof_pft_dif_z(:,:,:,:) = 0._r8 - currentPatch%rad_error(:) = 0._r8 + currentPatch%rad_error(:) = hlm_hio_ignore_val if_notbareground: if(currentPatch%nocomp_pft_label.ne.nocomp_bareground) then diff --git a/radiation/FatesRadiationDriveMod.F90 b/radiation/FatesRadiationDriveMod.F90 index 28bf779bf6..d65c852219 100644 --- a/radiation/FatesRadiationDriveMod.F90 +++ b/radiation/FatesRadiationDriveMod.F90 @@ -32,7 +32,7 @@ module FatesRadiationDriveMod use TwoStreamMLPEMod, only : normalized_upper_boundary use FatesTwoStreamUtilsMod, only : FatesPatchFSun use FatesTwoStreamUtilsMod, only : CheckPatchRadiationBalance - use FatesInterfaceTypesMod , only : hlm_hio_ignore_val + use FatesInterfaceTypesMod, only : hlm_hio_ignore_val use EDParamsMod , only : dinc_vai,dlower_vai use EDParamsMod , only : nclmax use EDParamsMod , only : nlevleaf @@ -132,7 +132,7 @@ subroutine FatesNormalizedCanopyRadiation(sites, bc_in, bc_out ) currentPatch%gnd_alb_dif(1:num_swb) = bc_in(s)%albgr_dif_rb(1:num_swb) currentPatch%gnd_alb_dir(1:num_swb) = bc_in(s)%albgr_dir_rb(1:num_swb) currentPatch%fcansno = bc_in(s)%fcansno_pa(ifp) - currentPatch%rad_error(:) = 0._r8 + currentPatch%rad_error(:) = hlm_hio_ignore_val if_zenith_flag: if( bc_in(s)%coszen>0._r8 )then From 3072e4b305dcae0c0f750d97dce824bf7f7b909c Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 15 May 2025 14:29:55 -0700 Subject: [PATCH 057/194] change name of new bc out flux subroutine for more detail --- biogeochem/EDPhysiologyMod.F90 | 6 +++--- main/EDMainMod.F90 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index cc62825072..a40d6ba16b 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -155,7 +155,7 @@ module EDPhysiologyMod public :: calculate_SP_properties public :: recruitment public :: ZeroLitterFluxes - public :: ZeroBCOutFluxes + public :: ZeroBCOutCarbonFluxes public :: ZeroAllocationRates public :: PreDisturbanceLitterFluxes @@ -232,7 +232,7 @@ end subroutine ZeroLitterFluxes ! ===================================================================================== - subroutine ZeroBCOutFluxes (bc_out) + subroutine ZeroBCOutCarbonFluxes (bc_out) ! !ARGUMENTS type(bc_out_type), intent(inout) :: bc_out @@ -242,7 +242,7 @@ subroutine ZeroBCOutFluxes (bc_out) bc_out%gpp_site = 0._r8 bc_out%ar_site = 0._r8 - end subroutine ZeroBCOutFluxes + end subroutine ZeroBCOutCarbonFluxes ! ===================================================================================== diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 929dbf44e9..15252d63c6 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -46,7 +46,7 @@ module EDMainMod use EDPhysiologyMod , only : SeedUpdate use EDPhysiologyMod , only : ZeroAllocationRates use EDPhysiologyMod , only : ZeroLitterFluxes - use EDPhysiologyMod , only : ZeroBCOutFluxes + use EDPhysiologyMod , only : ZeroBCOutCarbonFluxes use EDPhysiologyMod , only : PreDisturbanceLitterFluxes use EDPhysiologyMod , only : PreDisturbanceIntegrateLitter use EDPhysiologyMod , only : UpdateRecruitL2FR @@ -191,8 +191,8 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! Zero fluxes in and out of litter pools call ZeroLitterFluxes(currentSite) - ! Zero diagnostic bc_out fluxes - call ZeroBCOutFluxes(bc_out) + ! Zero diagnostic bc_out carbon fluxes + call ZeroBCOutCarbonFluxes(bc_out) ! Zero mass balance call TotalBalanceCheck(currentSite, 0) From c7cda2e8972757fdf1166edae8b30450b8065fa5 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 15 May 2025 14:47:35 -0700 Subject: [PATCH 058/194] add missing use statments for uptake --- parteh/PRTAllometricCNPMod.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index cf931d0a66..1b9b436742 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -53,6 +53,9 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : calloc_abs_error use FatesConstantsMod , only : nearzero use FatesConstantsMod , only : itrue + use FatesConstantsMod , only : ifalse + use FatesConstantsMod , only : coupled_p_uptake + use FatesConstantsMod , only : coupled_n_uptake use FatesConstantsMod , only : fates_unset_r8 use FatesConstantsMod , only : fates_unset_int use FatesConstantsMod , only : sec_per_day From 2284f9b846022cc481b2b667406c62efe464f2f7 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Wed, 21 May 2025 14:25:09 -0700 Subject: [PATCH 059/194] change crop PFT to back to cool C3 grass (PFT 13 now) --- parameter_files/fates_params_default.cdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 9fb97c811f..01fe718d59 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -1706,7 +1706,7 @@ data: fates_frag_cwd_frac = 0.045, 0.075, 0.21, 0.67 ; - fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 11 ; + fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 13 ; fates_landuse_grazing_rate = 0, 0, 0, 0, 0 ; From 26e2586a37eb015adcc76f85e5cea2ab338ba167 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Fri, 23 May 2025 17:00:03 -0700 Subject: [PATCH 060/194] remote bc_in argument from FuseCohortHydraulics --- biogeochem/EDCohortDynamicsMod.F90 | 2 +- biogeophys/FatesPlantHydraulicsMod.F90 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 1acc2a7835..c6582b32c8 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -941,7 +941,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%size_class,currentCohort%size_by_pft_class) if(hlm_use_planthydro.eq.itrue) then - call FuseCohortHydraulics(currentSite,currentCohort,nextc,bc_in,newn) + call FuseCohortHydraulics(currentSite,currentCohort,nextc,newn) endif ! recent canopy history diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index 013fbdf9f1..f390b73cce 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -1203,14 +1203,13 @@ end function constrain_water_contents ! ===================================================================================== -subroutine FuseCohortHydraulics(currentSite,currentCohort, nextCohort, bc_in, newn) +subroutine FuseCohortHydraulics(currentSite,currentCohort, nextCohort, newn) type(fates_cohort_type), intent(inout), target :: currentCohort ! current cohort type(fates_cohort_type), intent(inout), target :: nextCohort ! next (donor) cohort type(ed_site_type), intent(inout), target :: currentSite ! current site - type(bc_in_type), intent(in) :: bc_in real(r8), intent(in) :: newn ! !LOCAL VARIABLES: From 93f9c31bdf42fac7923eecf9aa108430780c5d02 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Fri, 23 May 2025 17:02:31 -0700 Subject: [PATCH 061/194] remove bc_in argument from UpdateSizeDepPlanyHydProps --- biogeochem/EDCohortDynamicsMod.F90 | 2 +- biogeophys/FatesPlantHydraulicsMod.F90 | 3 +-- main/EDMainMod.F90 | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index c6582b32c8..cfa30a0505 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1118,7 +1118,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) ! update hydraulics quantities that are functions of height & biomasses ! deallocate the hydro structure of nextc if (hlm_use_planthydro.eq.itrue) then - call UpdateSizeDepPlantHydProps(currentSite,currentCohort, bc_in) + call UpdateSizeDepPlantHydProps(currentSite,currentCohort) endif call nextc%FreeMemory() diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index f390b73cce..021c84672d 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -840,7 +840,7 @@ end subroutine SavePreviousCompartmentVolumes ! ===================================================================================== - subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort,bc_in) + subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort) ! DESCRIPTION: Updates absorbing root length (total and its vertical distribution) @@ -853,7 +853,6 @@ subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort,bc_in) ! ARGUMENTS: type(ed_site_type) , intent(in) :: currentSite ! Site stuff type(fates_cohort_type) , intent(inout) :: ccohort ! current cohort pointer - type(bc_in_type) , intent(in) :: bc_in ! Boundary Conditions ! Locals integer :: nlevrhiz ! Number of total soil layers diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index bfba6d7d56..be81177f1d 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -714,7 +714,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) ! (size --> heights of elements --> hydraulic path lengths --> ! maximum node-to-node conductances) if( (hlm_use_planthydro.eq.itrue) .and. do_growthrecruiteffects) then - call UpdateSizeDepPlantHydProps(currentSite,currentCohort, bc_in) + call UpdateSizeDepPlantHydProps(currentSite,currentCohort) call UpdateSizeDepPlantHydStates(currentSite,currentCohort) end if From 2ee65cd5706bfd6ed2924dec27253d7fe048f887 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 27 May 2025 14:41:01 -0700 Subject: [PATCH 062/194] remove unused bc_in from canopy_summarization --- biogeochem/EDCanopyStructureMod.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index f7d4782f15..1ce276f330 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -1307,7 +1307,7 @@ end subroutine canopy_spread ! ===================================================================================== - subroutine canopy_summarization( nsites, sites, bc_in ) + subroutine canopy_summarization( nsites, sites ) ! ---------------------------------------------------------------------------------- ! Much of this routine was once ed_clm_link minus all the IO and history stuff @@ -1323,7 +1323,6 @@ subroutine canopy_summarization( nsites, sites, bc_in ) ! !ARGUMENTS integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch From cd24a497d2682006ba9b8b61993ae48ebc4c3654 Mon Sep 17 00:00:00 2001 From: Charles D Koven Date: Fri, 23 May 2025 08:19:20 -0700 Subject: [PATCH 063/194] changed external seed rain to only fall on a given PFT's nocomp patch --- biogeochem/EDPhysiologyMod.F90 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 1f247de245..d608522b76 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2184,7 +2184,11 @@ subroutine SeedUpdate( currentSite ) ! Seed input from external sources (user param seed rain, or dispersal model) ! Include both prescribed seed_suppl and seed_in dispersed from neighbouring gridcells - seed_in_external = seed_stoich*(currentSite%seed_in(pft)/area + EDPftvarcon_inst%seed_suppl(pft)*years_per_day) ![kg/m2/day] + seed_in_external = seed_stoich * currentSite%seed_in(pft)/area ![kg/m2/day] + !only add external seed rain to a given PFT's nocomp patches + if ( (hlm_use_nocomp .eq. ifalse) .or. (hlm_use_nocomp .eq. itrue .and. currentPatch%nocomp_pft_label .eq. pft) ) then + seed_in_external = seed_in_external + seed_stoich * EDPftvarcon_inst%seed_suppl(pft)*years_per_day ![kg/m2/day] + endif litt%seed_in_extern(pft) = litt%seed_in_extern(pft) + seed_in_external ! Seeds entering externally [kg/site/day] From e43e22f3b4f91e647c77a862e57bcec4b5379dbf Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 29 May 2025 15:24:43 -0700 Subject: [PATCH 064/194] Revert "remove unused bc_in from canopy_summarization" This reverts commit 2ee65cd5706bfd6ed2924dec27253d7fe048f887. --- biogeochem/EDCanopyStructureMod.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 1ce276f330..f7d4782f15 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -1307,7 +1307,7 @@ end subroutine canopy_spread ! ===================================================================================== - subroutine canopy_summarization( nsites, sites ) + subroutine canopy_summarization( nsites, sites, bc_in ) ! ---------------------------------------------------------------------------------- ! Much of this routine was once ed_clm_link minus all the IO and history stuff @@ -1323,6 +1323,7 @@ subroutine canopy_summarization( nsites, sites ) ! !ARGUMENTS integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) + type(bc_in_type) , intent(in) :: bc_in(nsites) ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch From deb899420dcc53117b961b74b85530ed2c322c83 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 29 May 2025 15:51:11 -0700 Subject: [PATCH 065/194] remove unused bc_in arguments for history procedures --- main/FatesHistoryInterfaceMod.F90 | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 86d3bbeddb..0129030a2e 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -2344,10 +2344,10 @@ subroutine update_history_dyn(this,nc,nsites,sites,bc_in) if (hlm_use_ed_st3.eq.itrue) return if(hlm_hist_level_dynam>0) then - call update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) + call update_history_dyn_sitelevel(this,nc,nsites,sites) if(hlm_hist_level_dynam>1) then call update_history_dyn_subsite(this,nc,nsites,sites,bc_in) - call update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) + call update_history_dyn_subsite_ageclass(this,nc,nsites,sites) call reset_history_dyn_subsite(this, nsites, sites) end if end if @@ -2359,7 +2359,7 @@ end subroutine update_history_dyn ! ========================================================================= - subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) + subroutine update_history_dyn_sitelevel(this,nc,nsites,sites) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -2373,7 +2373,6 @@ subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) type(fates_cohort_type), pointer :: ccohort type(fates_patch_type), pointer :: cpatch @@ -4678,7 +4677,7 @@ end subroutine update_history_dyn_subsite ! ========================================================================================= - subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) + subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -4692,7 +4691,6 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) type(fates_cohort_type), pointer :: ccohort type(fates_patch_type), pointer :: cpatch @@ -5008,7 +5006,7 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) if(hlm_hist_level_hifrq>0) then call update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) if(hlm_hist_level_hifrq>1) then - call update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + call update_history_hifrq_subsite(this,nc,nsites,sites,bc_out,dt_tstep) call update_history_hifrq_subsite_ageclass(this,nsites,sites,dt_tstep) end if end if @@ -5240,7 +5238,7 @@ end subroutine update_history_hifrq_sitelevel ! =============================================================================================== - subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_out,dt_tstep) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -5256,7 +5254,6 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep From 2546661cefb0b086e7b75540cd02c87ce7a192d3 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 29 May 2025 15:52:57 -0700 Subject: [PATCH 066/194] remove unused bc_in arguments from seed related procedures --- biogeochem/EDPhysiologyMod.F90 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 58e2a0bc2c..68f5790933 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -474,12 +474,12 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) diag => currentSite%flux_diags%elem(el)) ! Calculate loss rate of viable seeds to litter - call SeedDecay(litt, currentPatch, bc_in) + call SeedDecay(litt, currentPatch) ! Calculate seed germination rate, the status flags prevent ! germination from occuring when the site is in a drought ! (for drought deciduous) or too cold (for cold deciduous) - call SeedGermination(litt, currentSite%cstatus, currentSite%dstatus(1:numpft), bc_in, currentPatch) + call SeedGermination(litt, currentSite%cstatus, currentSite%dstatus(1:numpft), currentPatch) ! Send fluxes from newly created litter into the litter pools ! This litter flux is from non-disturbance inducing mortality, as well @@ -2206,7 +2206,7 @@ end subroutine SeedUpdate ! ============================================================================ - subroutine SeedDecay( litt , currentPatch, bc_in ) + subroutine SeedDecay( litt , currentPatch ) ! ! !DESCRIPTION: ! 1. Flux from seed pool into leaf litter pool @@ -2217,7 +2217,6 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) ! !ARGUMENTS type(litter_type) :: litt type(fates_patch_type), intent(in) :: currentPatch ! ahb added this - type(bc_in_type), intent(in) :: bc_in ! ahb added this ! ! !LOCAL VARIABLES: integer :: pft @@ -2323,7 +2322,7 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) end subroutine SeedDecay ! ============================================================================ - subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) + subroutine SeedGermination( litt, cold_stat, drought_stat, currentPatch ) ! ! !DESCRIPTION: ! Flux from seed bank into the seedling pool @@ -2335,7 +2334,6 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) type(litter_type) :: litt integer , intent(in) :: cold_stat ! Is the site in cold leaf-off status? integer, dimension(numpft), intent(in) :: drought_stat ! Is the site in drought leaf-off status? - type(bc_in_type), intent(in) :: bc_in type(fates_patch_type), intent(in) :: currentPatch ! ! !LOCAL VARIABLES: From bb3e55258f25dd5155b590b00f83a3ed3f95a69e Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 29 May 2025 15:54:27 -0700 Subject: [PATCH 067/194] remove unused bc_in from recruitwuptake procedure --- biogeophys/FatesPlantHydraulicsMod.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index 021c84672d..527451720e 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -1787,7 +1787,7 @@ subroutine UpdateH2OVeg(csite,bc_out,prev_site_h2o,icall) end subroutine UpdateH2OVeg !===================================================================================== -subroutine RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) +subroutine RecruitWUptake(nsites,sites,dtime,recruitflag) ! ---------------------------------------------------------------------------------- ! This subroutine is called to calculate the water requirement for newly recruited cohorts @@ -1802,7 +1802,6 @@ subroutine RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) ! Arguments integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) - type(bc_in_type), intent(in) :: bc_in(nsites) real(r8), intent(in) :: dtime !time (seconds) logical, intent(out) :: recruitflag !flag to check if there is newly recruited cohorts @@ -2442,7 +2441,7 @@ subroutine hydraulics_bc ( nsites, sites, bc_in, bc_out, dtime) ! ---------------------------------------------------------------------------------- !For newly recruited cohorts, add the water uptake demand to csite_hydr%recruit_w_uptake - call RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) + call RecruitWUptake(nsites,sites,dtime,recruitflag) !update water storage in veg after incorporating newly recuited cohorts if(recruitflag)then From bd63aa457b57a498382977d72c8451e273d5a91c Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 29 May 2025 15:55:48 -0700 Subject: [PATCH 068/194] remove unused bc_in from damage and litter flux procedure --- biogeochem/EDPhysiologyMod.F90 | 5 ++--- main/EDMainMod.F90 | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 68f5790933..9c00edfc3a 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -260,13 +260,12 @@ end subroutine ZeroAllocationRates ! ============================================================================ - subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) + subroutine GenerateDamageAndLitterFluxes( csite, cpatch ) ! Arguments type(ed_site_type) :: csite type(fates_patch_type) :: cpatch - type(bc_in_type), intent(in) :: bc_in - + ! Locals type(fates_cohort_type), pointer :: ccohort ! Current cohort diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index be81177f1d..7bbc2e528b 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -782,7 +782,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentPatch => currentSite%youngest_patch do while(associated(currentPatch)) - call GenerateDamageAndLitterFluxes( currentSite, currentPatch, bc_in) + call GenerateDamageAndLitterFluxes( currentSite, currentPatch) call PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in) From 9c90594fdb35cfe4ab916212e12dc2c42f995abd Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Wed, 4 Jun 2025 16:54:02 -0700 Subject: [PATCH 069/194] remove unused bc_out in history sub procedures --- main/FatesHistoryInterfaceMod.F90 | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 0129030a2e..fd1f2449cd 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -5004,9 +5004,9 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) real(r8) , intent(in) :: dt_tstep if(hlm_hist_level_hifrq>0) then - call update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + call update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,dt_tstep) if(hlm_hist_level_hifrq>1) then - call update_history_hifrq_subsite(this,nc,nsites,sites,bc_out,dt_tstep) + call update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) call update_history_hifrq_subsite_ageclass(this,nsites,sites,dt_tstep) end if end if @@ -5015,7 +5015,7 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) return end subroutine update_history_hifrq - subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,dt_tstep) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -5030,7 +5030,6 @@ subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_t integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) type(bc_in_type) , intent(in) :: bc_in(nsites) - type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep ! Locals @@ -5238,7 +5237,7 @@ end subroutine update_history_hifrq_sitelevel ! =============================================================================================== - subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_out,dt_tstep) + subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -5254,7 +5253,6 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_out,dt_tstep) integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep ! Locals From 8ee8e2d96c820f4143006e309fda0678d5d65f67 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 5 Jun 2025 06:59:48 -0700 Subject: [PATCH 070/194] NBP updates --- biogeochem/EDPatchDynamicsMod.F90 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 65b5f8def7..75245b94a3 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -1075,10 +1075,17 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) currentSite%flux_diags%elem(el)%burned_liveveg + & leaf_burn_frac * leaf_m * nc%n * area_inv - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & - leaf_burn_frac * leaf_m * nc%n * ha_per_m2 * days_per_sec + end do + ! Add burned leaf carbon to the atmospheric carbon flux + ! for burning. + ! [frac/day]*[kgC/plant]*[plant/ha]*[m2/ha]*[day/s] = [kg/m2/s] + + bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & + leaf_burn_frac * nc%prt%GetState(leaf_organ, carbon12_element) * & + nc%n * ha_per_m2 * days_per_sec + ! Here the mass is removed from the plant if(int(prt_params%woody(currentCohort%pft)) == itrue)then From c489196f6c2672d86c86bf29d1ea24f5e4e33daa Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 5 Jun 2025 09:48:29 -0700 Subject: [PATCH 071/194] remove unused bc_out argument for FillDrainRhizShells --- biogeophys/FatesPlantHydraulicsMod.F90 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index 527451720e..6ec6148a74 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -294,7 +294,7 @@ subroutine hydraulics_drive( nsites, sites, bc_in,bc_out,dtime ) case (1) - call FillDrainRhizShells(nsites, sites, bc_in, bc_out ) + call FillDrainRhizShells(nsites, sites, bc_in) call hydraulics_BC(nsites, sites,bc_in,bc_out,dtime ) case (2) @@ -2183,7 +2183,7 @@ end subroutine BTranForHLMDiagnosticsFromCohortHydr ! ========================================================================== -subroutine FillDrainRhizShells(nsites, sites, bc_in, bc_out) +subroutine FillDrainRhizShells(nsites, sites, bc_in) ! ! Created by Brad Christoffersen, Jan 2016 ! @@ -2209,7 +2209,6 @@ subroutine FillDrainRhizShells(nsites, sites, bc_in, bc_out) integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) type(bc_in_type), intent(in) :: bc_in(nsites) - type(bc_out_type), intent(inout) :: bc_out(nsites) ! Locals type(ed_site_hydr_type), pointer :: csite_hydr ! pointer to site hydraulics object From a47a95a510c51d0fb87e83a49d9a52669d0549e9 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 5 Jun 2025 11:47:17 -0700 Subject: [PATCH 072/194] remove unused bc_out in RecruitWaterStorage --- biogeophys/FatesPlantHydraulicsMod.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index 6ec6148a74..1672ec5ff8 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -4310,7 +4310,7 @@ end subroutine AccumulateMortalityWaterStorage !-------------------------------------------------------------------------------! -subroutine RecruitWaterStorage(nsites,sites,bc_out) +subroutine RecruitWaterStorage(nsites,sites) ! --------------------------------------------------------------------------- ! This subroutine accounts for the water bound in plants that have @@ -4325,7 +4325,6 @@ subroutine RecruitWaterStorage(nsites,sites,bc_out) ! Arguments integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) - type(bc_out_type), intent(inout) :: bc_out(nsites) ! Locals type(fates_cohort_type), pointer :: currentCohort From 4675414e954ce4592ee17233d7b20b9de56cf793 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 5 Jun 2025 11:58:05 -0700 Subject: [PATCH 073/194] update recruitwaterstorage call --- biogeochem/EDCanopyStructureMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index f7d4782f15..83f10a26b5 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -2075,7 +2075,7 @@ subroutine update_hlm_dynamics(nsites,sites,fcolumn,bc_out) ! call during the fast timestep sequence if (hlm_use_planthydro.eq.itrue) then - call RecruitWaterStorage(nsites,sites,bc_out) + call RecruitWaterStorage(nsites,sites) end if end subroutine update_hlm_dynamics From 536d9524dc7eb9d498ff7518774d2eb922e5e84f Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 5 Jun 2025 16:40:51 -0700 Subject: [PATCH 074/194] pull nocomp_seed_scaling default out of check to avoid some compiler warnings --- biogeochem/EDPhysiologyMod.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 53f60a1a2c..d309c38fdc 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2172,14 +2172,13 @@ subroutine SeedUpdate( currentSite ) ! special case: do we want to restrict each PFT's seeds to only go to patches with that nocomp PFT label? ! If so, then use a normalization factor that is one over the nocomp patch fraction for all patches of ! that PFT's nocomp label, and zero for all other patches. If we don't do this, then just set scalar to one. + nocomp_seed_scaling = 1._r8 if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then if (currentPatch%nocomp_pft_label .eq. pft) then nocomp_seed_scaling = AREA/nocomp_patch_areas(pft) else nocomp_seed_scaling = 0._r8 endif - else - nocomp_seed_scaling = 1._r8 endif ! Seed input from local sources (within site). Note that a fraction of the From 7929e6e11fcc3244743f0178a12be130e7cff836 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Fri, 6 Jun 2025 09:55:00 -0700 Subject: [PATCH 075/194] remove homogenize_seed_pfts option This is a very niche use-case that was hard-coded that contrasts with nocomp seed handling. Given this and its percieved lack of use, it is being removed from the code. --- biogeochem/EDPatchDynamicsMod.F90 | 9 ++------- biogeochem/EDPhysiologyMod.F90 | 8 -------- main/EDTypesMod.F90 | 1 - 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index b1fc9af66d..e79d2756be 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -17,7 +17,6 @@ module EDPatchDynamicsMod use FatesLitterMod , only : litter_type use FatesConstantsMod , only : n_dbh_bins use FatesLitterMod , only : adjust_SF_CWD_frac - use EDTypesMod , only : homogenize_seed_pfts use EDTypesMod , only : area use FatesConstantsMod , only : patchfusion_dbhbin_loweredges use EDtypesMod , only : force_patchfuse_min_biomass @@ -3578,12 +3577,8 @@ subroutine DistributeSeeds(currentSite,seed_mass,el,pft) do while(associated(currentPatch)) litt => currentPatch%litter(el) - if(homogenize_seed_pfts) then - litt%seed(:) = litt%seed(:) + seed_mass/(area_site*real(numpft,r8)) - else - litt%seed(pft) = litt%seed(pft) + seed_mass/area_site - end if - + litt%seed(pft) = litt%seed(pft) + seed_mass/area_site + currentPatch => currentPatch%younger end do diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index d309c38fdc..8684ab4d4e 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2056,7 +2056,6 @@ subroutine SeedUpdate( currentSite ) ! !USES: use EDTypesMod, only : area - use EDTypesMod, only : homogenize_seed_pfts use FatesInterfaceTypesMod, only : hlm_seeddisp_cadence use FatesInterfaceTypesMod, only : fates_dispersal_cadence_none ! @@ -2151,13 +2150,6 @@ subroutine SeedUpdate( currentSite ) currentPatch => currentPatch%younger enddo seed_rain_loop - ! We can choose to homogenize seeds. This is simple, we just - ! add up all the seed from each pft at the site level, and then - ! equally distribute to the PFT pools - if ( homogenize_seed_pfts ) then - site_seed_rain(1:numpft) = sum(site_seed_rain(:))/real(numpft,r8) - end if - ! Loop over all patches again and disperse the mixed seeds into the input flux ! arrays ! Loop over all patches and sum up the seed input for each PFT diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index c21cdd6fe1..701a95eeb2 100644 --- a/main/EDTypesMod.F90 +++ b/main/EDTypesMod.F90 @@ -129,7 +129,6 @@ module EDTypesMod ! number densities of cohorts to prevent FPEs ! special mode to cause PFTs to create seed mass of all currently-existing PFTs - logical, parameter, public :: homogenize_seed_pfts = .false. character(len=*), parameter, private :: sourcefile = __FILE__ !************************************ From 169f08c9bb8b97ee065fc070e495dd0ef799b014 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Fri, 6 Jun 2025 16:20:26 -0700 Subject: [PATCH 076/194] Remove deprecated quadratic smoothing parameters ### Description: ### Collaborators: ### Expectation of Answer Changes: ### Checklist *If this is your first time contributing, please read the [**CONTRIBUTING**](https://github.com/NGEET/fates/blob/main/CONTRIBUTING.md) document.* All checklist items must be checked to enable merging this pull request: *Contributor* - [ ] The in-code documentation has been updated with descriptive comments - [ ] The documentation has been assessed to determine if updates are necessary *Integrator* - [ ] FATES PASS/FAIL regression tests were run - [ ] Evaluation of test results for answer changes was performed and results provided ### Documentation - [Technical Note](https://github.com/NGEET/fates-docs) update: - [User's Guide](https://github.com/NGEET/fates-users-guide) update: ### Test Results: *CTSM (or) E3SM (specify which) test hash-tag:* *CTSM (or) E3SM (specify which) baseline hash-tag:* *FATES baseline hash-tag:* *Test Output:* --- .../api40.0.0_060625_params_default.cdl | 1844 +++++++++++++++++ .../archive/api41.0.0_prxxx_patch_params.xml | 32 + 2 files changed, 1876 insertions(+) create mode 100644 parameter_files/archive/api40.0.0_060625_params_default.cdl create mode 100644 parameter_files/archive/api41.0.0_prxxx_patch_params.xml diff --git a/parameter_files/archive/api40.0.0_060625_params_default.cdl b/parameter_files/archive/api40.0.0_060625_params_default.cdl new file mode 100644 index 0000000000..9fb97c811f --- /dev/null +++ b/parameter_files/archive/api40.0.0_060625_params_default.cdl @@ -0,0 +1,1844 @@ +netcdf fates_params_default { +dimensions: + fates_NCWD = 4 ; + fates_history_age_bins = 7 ; + fates_history_coage_bins = 2 ; + fates_history_damage_bins = 2 ; + fates_history_height_bins = 6 ; + fates_history_size_bins = 13 ; + fates_hlm_pftno = 14 ; + fates_hydr_organs = 4 ; + fates_landuseclass = 5 ; + fates_leafage_class = 1 ; + fates_litterclass = 6 ; + fates_pft = 14 ; + fates_plant_organs = 4 ; + fates_string_length = 60 ; +variables: + double fates_history_ageclass_bin_edges(fates_history_age_bins) ; + fates_history_ageclass_bin_edges:units = "yr" ; + fates_history_ageclass_bin_edges:long_name = "Lower edges for age class bins used in age-resolved patch history output" ; + double fates_history_coageclass_bin_edges(fates_history_coage_bins) ; + fates_history_coageclass_bin_edges:units = "years" ; + fates_history_coageclass_bin_edges:long_name = "Lower edges for cohort age class bins used in cohort age resolved history output" ; + double fates_history_height_bin_edges(fates_history_height_bins) ; + fates_history_height_bin_edges:units = "m" ; + fates_history_height_bin_edges:long_name = "Lower edges for height bins used in height-resolved history output" ; + double fates_history_damage_bin_edges(fates_history_damage_bins) ; + fates_history_damage_bin_edges:units = "% crown loss" ; + fates_history_damage_bin_edges:long_name = "Lower edges for damage class bins used in cohort history output" ; + double fates_history_sizeclass_bin_edges(fates_history_size_bins) ; + fates_history_sizeclass_bin_edges:units = "cm" ; + fates_history_sizeclass_bin_edges:long_name = "Lower edges for DBH size class bins used in size-resolved cohort history output" ; + double fates_alloc_organ_id(fates_plant_organs) ; + fates_alloc_organ_id:units = "unitless" ; + fates_alloc_organ_id:long_name = "This is the global index that the organ in this file is associated with, values match those in parteh/PRTGenericMod.F90" ; + double fates_hydro_htftype_node(fates_hydr_organs) ; + fates_hydro_htftype_node:units = "unitless" ; + fates_hydro_htftype_node:long_name = "Switch that defines the hydraulic transfer functions for each organ." ; + char fates_pftname(fates_pft, fates_string_length) ; + fates_pftname:units = "unitless - string" ; + fates_pftname:long_name = "Description of plant type" ; + char fates_hydro_organ_name(fates_hydr_organs, fates_string_length) ; + fates_hydro_organ_name:units = "unitless - string" ; + fates_hydro_organ_name:long_name = "Name of plant hydraulics organs (DONT CHANGE, order matches media list in FatesHydraulicsMemMod.F90)" ; + char fates_alloc_organ_name(fates_plant_organs, fates_string_length) ; + fates_alloc_organ_name:units = "unitless - string" ; + fates_alloc_organ_name:long_name = "Name of plant organs (with alloc_organ_id, must match PRTGenericMod.F90)" ; + char fates_landuseclass_name(fates_landuseclass, fates_string_length) ; + fates_landuseclass_name:units = "unitless - string" ; + fates_landuseclass_name:long_name = "Name of the land use classes, for variables associated with dimension fates_landuseclass" ; + char fates_litterclass_name(fates_litterclass, fates_string_length) ; + fates_litterclass_name:units = "unitless - string" ; + fates_litterclass_name:long_name = "Name of the litter classes, for variables associated with dimension fates_litterclass" ; + double fates_alloc_organ_priority(fates_plant_organs, fates_pft) ; + fates_alloc_organ_priority:units = "index" ; + fates_alloc_organ_priority:long_name = "Priority level for allocation, 1: replaces turnover from storage, 2: same priority as storage use/replacement, 3: ascending in order of least importance" ; + double fates_alloc_storage_cushion(fates_pft) ; + fates_alloc_storage_cushion:units = "fraction" ; + fates_alloc_storage_cushion:long_name = "maximum size of storage C pool, relative to maximum size of leaf C pool" ; + double fates_alloc_store_priority_frac(fates_pft) ; + fates_alloc_store_priority_frac:units = "unitless" ; + fates_alloc_store_priority_frac:long_name = "for high-priority organs, the fraction of their turnover demand that is gauranteed to be replaced, and if need-be by storage" ; + double fates_allom_agb1(fates_pft) ; + fates_allom_agb1:units = "variable" ; + fates_allom_agb1:long_name = "Parameter 1 for agb allometry" ; + double fates_allom_agb2(fates_pft) ; + fates_allom_agb2:units = "variable" ; + fates_allom_agb2:long_name = "Parameter 2 for agb allometry" ; + double fates_allom_agb3(fates_pft) ; + fates_allom_agb3:units = "variable" ; + fates_allom_agb3:long_name = "Parameter 3 for agb allometry" ; + double fates_allom_agb4(fates_pft) ; + fates_allom_agb4:units = "variable" ; + fates_allom_agb4:long_name = "Parameter 4 for agb allometry" ; + double fates_allom_agb_frac(fates_pft) ; + fates_allom_agb_frac:units = "fraction" ; + fates_allom_agb_frac:long_name = "Fraction of woody biomass that is above ground" ; + double fates_allom_amode(fates_pft) ; + fates_allom_amode:units = "index" ; + fates_allom_amode:long_name = "AGB allometry function index." ; + double fates_allom_blca_expnt_diff(fates_pft) ; + fates_allom_blca_expnt_diff:units = "unitless" ; + fates_allom_blca_expnt_diff:long_name = "difference between allometric DBH:bleaf and DBH:crown area exponents" ; + double fates_allom_cmode(fates_pft) ; + fates_allom_cmode:units = "index" ; + fates_allom_cmode:long_name = "coarse root biomass allometry function index." ; + double fates_allom_d2bl1(fates_pft) ; + fates_allom_d2bl1:units = "variable" ; + fates_allom_d2bl1:long_name = "Parameter 1 for d2bl allometry" ; + double fates_allom_d2bl2(fates_pft) ; + fates_allom_d2bl2:units = "variable" ; + fates_allom_d2bl2:long_name = "Parameter 2 for d2bl allometry" ; + double fates_allom_d2bl3(fates_pft) ; + fates_allom_d2bl3:units = "unitless" ; + fates_allom_d2bl3:long_name = "Parameter 3 for d2bl allometry" ; + double fates_allom_d2ca_coefficient_max(fates_pft) ; + fates_allom_d2ca_coefficient_max:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_max:long_name = "max (savanna) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2ca_coefficient_min(fates_pft) ; + fates_allom_d2ca_coefficient_min:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_min:long_name = "min (forest) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2h1(fates_pft) ; + fates_allom_d2h1:units = "variable" ; + fates_allom_d2h1:long_name = "Parameter 1 for d2h allometry (intercept, or c)" ; + double fates_allom_d2h2(fates_pft) ; + fates_allom_d2h2:units = "variable" ; + fates_allom_d2h2:long_name = "Parameter 2 for d2h allometry (slope, or m)" ; + double fates_allom_d2h3(fates_pft) ; + fates_allom_d2h3:units = "variable" ; + fates_allom_d2h3:long_name = "Parameter 3 for d2h allometry (optional)" ; + double fates_allom_dbh_maxheight(fates_pft) ; + fates_allom_dbh_maxheight:units = "cm" ; + fates_allom_dbh_maxheight:long_name = "the diameter (if any) corresponding to maximum height, diameters may increase beyond this" ; + double fates_allom_dmode(fates_pft) ; + fates_allom_dmode:units = "index" ; + fates_allom_dmode:long_name = "crown depth allometry function index" ; + double fates_allom_fmode(fates_pft) ; + fates_allom_fmode:units = "index" ; + fates_allom_fmode:long_name = "fine root biomass allometry function index." ; + double fates_allom_fnrt_prof_a(fates_pft) ; + fates_allom_fnrt_prof_a:units = "unitless" ; + fates_allom_fnrt_prof_a:long_name = "Fine root profile function, parameter a" ; + double fates_allom_fnrt_prof_b(fates_pft) ; + fates_allom_fnrt_prof_b:units = "unitless" ; + fates_allom_fnrt_prof_b:long_name = "Fine root profile function, parameter b" ; + double fates_allom_fnrt_prof_mode(fates_pft) ; + fates_allom_fnrt_prof_mode:units = "index" ; + fates_allom_fnrt_prof_mode:long_name = "Index to select fine root profile function: 1) Jackson Beta, 2) 1-param exponential 3) 2-param exponential" ; + double fates_allom_frbstor_repro(fates_pft) ; + fates_allom_frbstor_repro:units = "fraction" ; + fates_allom_frbstor_repro:long_name = "fraction of bstore goes to reproduction after plant dies" ; + double fates_allom_h2cd1(fates_pft) ; + fates_allom_h2cd1:units = "variable" ; + fates_allom_h2cd1:long_name = "Parameter 1 for h2cd allometry (exp(log-intercept) or scaling). If allom_dmode=1; this is the same as former crown_depth_frac parameter" ; + double fates_allom_h2cd2(fates_pft) ; + fates_allom_h2cd2:units = "variable" ; + fates_allom_h2cd2:long_name = "Parameter 2 for h2cd allometry (log-slope or exponent). If allom_dmode=1; this is not needed (as exponent is assumed 1)" ; + double fates_allom_hmode(fates_pft) ; + fates_allom_hmode:units = "index" ; + fates_allom_hmode:long_name = "height allometry function index." ; + double fates_allom_l2fr(fates_pft) ; + fates_allom_l2fr:units = "gC/gC" ; + fates_allom_l2fr:long_name = "Allocation parameter: fine root C per leaf C" ; + double fates_allom_la_per_sa_int(fates_pft) ; + fates_allom_la_per_sa_int:units = "m2/cm2" ; + fates_allom_la_per_sa_int:long_name = "Leaf area per sapwood area, intercept" ; + double fates_allom_la_per_sa_slp(fates_pft) ; + fates_allom_la_per_sa_slp:units = "m2/cm2/m" ; + fates_allom_la_per_sa_slp:long_name = "Leaf area per sapwood area rate of change with height, slope (optional)" ; + double fates_allom_lmode(fates_pft) ; + fates_allom_lmode:units = "index" ; + fates_allom_lmode:long_name = "leaf biomass allometry function index." ; + double fates_allom_sai_scaler(fates_pft) ; + fates_allom_sai_scaler:units = "m2/m2" ; + fates_allom_sai_scaler:long_name = "allometric ratio of SAI per LAI" ; + double fates_allom_smode(fates_pft) ; + fates_allom_smode:units = "index" ; + fates_allom_smode:long_name = "sapwood allometry function index." ; + double fates_allom_stmode(fates_pft) ; + fates_allom_stmode:units = "index" ; + fates_allom_stmode:long_name = "storage allometry function index: 1) Storage proportional to leaf biomass (with trimming), 2) Storage proportional to maximum leaf biomass (not trimmed)" ; + double fates_allom_zroot_k(fates_pft) ; + fates_allom_zroot_k:units = "unitless" ; + fates_allom_zroot_k:long_name = "scale coefficient of logistic rooting depth model" ; + double fates_allom_zroot_max_dbh(fates_pft) ; + fates_allom_zroot_max_dbh:units = "cm" ; + fates_allom_zroot_max_dbh:long_name = "dbh at which a plant reaches the maximum value for its maximum rooting depth" ; + double fates_allom_zroot_max_z(fates_pft) ; + fates_allom_zroot_max_z:units = "m" ; + fates_allom_zroot_max_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_max_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_allom_zroot_min_dbh(fates_pft) ; + fates_allom_zroot_min_dbh:units = "cm" ; + fates_allom_zroot_min_dbh:long_name = "dbh at which the maximum rooting depth for a recruit is defined" ; + double fates_allom_zroot_min_z(fates_pft) ; + fates_allom_zroot_min_z:units = "m" ; + fates_allom_zroot_min_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_min_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_c2b(fates_pft) ; + fates_c2b:units = "ratio" ; + fates_c2b:long_name = "Carbon to biomass multiplier of bulk structural tissues" ; + double fates_cnp_eca_alpha_ptase(fates_pft) ; + fates_cnp_eca_alpha_ptase:units = "g/m3" ; + fates_cnp_eca_alpha_ptase:long_name = "(INACTIVE, KEEP AT 0) fraction of P from ptase activity sent directly to plant (ECA)" ; + double fates_cnp_eca_decompmicc(fates_pft) ; + fates_cnp_eca_decompmicc:units = "gC/m3" ; + fates_cnp_eca_decompmicc:long_name = "maximum soil microbial decomposer biomass found over depth (will be applied at a reference depth w/ exponential attenuation) (ECA)" ; + double fates_cnp_eca_km_nh4(fates_pft) ; + fates_cnp_eca_km_nh4:units = "gN/m3" ; + fates_cnp_eca_km_nh4:long_name = "half-saturation constant for plant nh4 uptake (ECA)" ; + double fates_cnp_eca_km_no3(fates_pft) ; + fates_cnp_eca_km_no3:units = "gN/m3" ; + fates_cnp_eca_km_no3:long_name = "half-saturation constant for plant no3 uptake (ECA)" ; + double fates_cnp_eca_km_p(fates_pft) ; + fates_cnp_eca_km_p:units = "gP/m3" ; + fates_cnp_eca_km_p:long_name = "half-saturation constant for plant p uptake (ECA)" ; + double fates_cnp_eca_km_ptase(fates_pft) ; + fates_cnp_eca_km_ptase:units = "gP/m3" ; + fates_cnp_eca_km_ptase:long_name = "half-saturation constant for biochemical P (ECA)" ; + double fates_cnp_eca_lambda_ptase(fates_pft) ; + fates_cnp_eca_lambda_ptase:units = "g/m3" ; + fates_cnp_eca_lambda_ptase:long_name = "(INACTIVE, KEEP AT 0) critical value for biochemical production (ECA)" ; + double fates_cnp_eca_vmax_ptase(fates_pft) ; + fates_cnp_eca_vmax_ptase:units = "gP/m2/s" ; + fates_cnp_eca_vmax_ptase:long_name = "maximum production rate for biochemical P (per m2) (ECA)" ; + double fates_cnp_nfix1(fates_pft) ; + fates_cnp_nfix1:units = "fraction" ; + fates_cnp_nfix1:long_name = "fractional surcharge added to maintenance respiration that drives symbiotic fixation" ; + double fates_cnp_nitr_store_ratio(fates_pft) ; + fates_cnp_nitr_store_ratio:units = "(gN/gN)" ; + fates_cnp_nitr_store_ratio:long_name = "storeable (labile) N, as a ratio compared to the N bound in cell structures of other organs (see code)" ; + double fates_cnp_phos_store_ratio(fates_pft) ; + fates_cnp_phos_store_ratio:units = "(gP/gP)" ; + fates_cnp_phos_store_ratio:long_name = "storeable (labile) P, as a ratio compared to the P bound in cell structures of other organs (see code)" ; + double fates_cnp_pid_kd(fates_pft) ; + fates_cnp_pid_kd:units = "unknown" ; + fates_cnp_pid_kd:long_name = "derivative constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_ki(fates_pft) ; + fates_cnp_pid_ki:units = "unknown" ; + fates_cnp_pid_ki:long_name = "integral constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_kp(fates_pft) ; + fates_cnp_pid_kp:units = "unknown" ; + fates_cnp_pid_kp:long_name = "proportional constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_prescribed_nuptake(fates_pft) ; + fates_cnp_prescribed_nuptake:units = "fraction" ; + fates_cnp_prescribed_nuptake:long_name = "Prescribed N uptake flux. 0=fully coupled simulation >0=prescribed (experimental)" ; + double fates_cnp_prescribed_puptake(fates_pft) ; + fates_cnp_prescribed_puptake:units = "fraction" ; + fates_cnp_prescribed_puptake:long_name = "Prescribed P uptake flux. 0=fully coupled simulation, >0=prescribed (experimental)" ; + double fates_cnp_store_ovrflw_frac(fates_pft) ; + fates_cnp_store_ovrflw_frac:units = "fraction" ; + fates_cnp_store_ovrflw_frac:long_name = "size of overflow storage (for excess C,N or P) as a fraction of storage target" ; + double fates_cnp_turnover_nitr_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_nitr_retrans:units = "fraction" ; + fates_cnp_turnover_nitr_retrans:long_name = "retranslocation (reabsorbtion) fraction of nitrogen in turnover of scenescing tissues" ; + double fates_cnp_turnover_phos_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_phos_retrans:units = "fraction" ; + fates_cnp_turnover_phos_retrans:long_name = "retranslocation (reabsorbtion) fraction of phosphorus in turnover of scenescing tissues" ; + double fates_cnp_vmax_nh4(fates_pft) ; + fates_cnp_vmax_nh4:units = "gN/gC/s" ; + fates_cnp_vmax_nh4:long_name = "maximum (potential) uptake rate of NH4 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_nh4 for usage)" ; + double fates_cnp_vmax_no3(fates_pft) ; + fates_cnp_vmax_no3:units = "gN/gC/s" ; + fates_cnp_vmax_no3:long_name = "maximum (potential) uptake rate of NO3 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_no3 for usage)" ; + double fates_cnp_vmax_p(fates_pft) ; + fates_cnp_vmax_p:units = "gP/gC/s" ; + fates_cnp_vmax_p:long_name = "maximum production rate for phosphorus (ECA and RD)" ; + double fates_damage_frac(fates_pft) ; + fates_damage_frac:units = "fraction" ; + fates_damage_frac:long_name = "fraction of cohort damaged in each damage event (event frequency specified in the is_it_damage_time subroutine)" ; + double fates_damage_mort_p1(fates_pft) ; + fates_damage_mort_p1:units = "fraction" ; + fates_damage_mort_p1:long_name = "inflection point of damage mortality function, a value of 0.8 means 50% mortality with 80% loss of crown, turn off with a large number" ; + double fates_damage_mort_p2(fates_pft) ; + fates_damage_mort_p2:units = "unitless" ; + fates_damage_mort_p2:long_name = "rate of mortality increase with damage" ; + double fates_damage_recovery_scalar(fates_pft) ; + fates_damage_recovery_scalar:units = "unitless" ; + fates_damage_recovery_scalar:long_name = "fraction of the cohort that recovers from damage" ; + double fates_dev_arbitrary_pft(fates_pft) ; + fates_dev_arbitrary_pft:units = "unknown" ; + fates_dev_arbitrary_pft:long_name = "Unassociated pft dimensioned free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_alpha_SH(fates_pft) ; + fates_fire_alpha_SH:units = "m / (kw/m)**(2/3)" ; + fates_fire_alpha_SH:long_name = "spitfire parameter, alpha scorch height, Equation 16 Thonicke et al 2010" ; + double fates_fire_bark_scaler(fates_pft) ; + fates_fire_bark_scaler:units = "fraction" ; + fates_fire_bark_scaler:long_name = "the thickness of a cohorts bark as a fraction of its dbh" ; + double fates_fire_crown_kill(fates_pft) ; + fates_fire_crown_kill:units = "NA" ; + fates_fire_crown_kill:long_name = "fire parameter, see equation 22 in Thonicke et al 2010" ; + double fates_frag_fnrt_fcel(fates_pft) ; + fates_frag_fnrt_fcel:units = "fraction" ; + fates_frag_fnrt_fcel:long_name = "Fine root litter cellulose fraction" ; + double fates_frag_fnrt_flab(fates_pft) ; + fates_frag_fnrt_flab:units = "fraction" ; + fates_frag_fnrt_flab:long_name = "Fine root litter labile fraction" ; + double fates_frag_fnrt_flig(fates_pft) ; + fates_frag_fnrt_flig:units = "fraction" ; + fates_frag_fnrt_flig:long_name = "Fine root litter lignin fraction" ; + double fates_frag_leaf_fcel(fates_pft) ; + fates_frag_leaf_fcel:units = "fraction" ; + fates_frag_leaf_fcel:long_name = "Leaf litter cellulose fraction" ; + double fates_frag_leaf_flab(fates_pft) ; + fates_frag_leaf_flab:units = "fraction" ; + fates_frag_leaf_flab:long_name = "Leaf litter labile fraction" ; + double fates_frag_leaf_flig(fates_pft) ; + fates_frag_leaf_flig:units = "fraction" ; + fates_frag_leaf_flig:long_name = "Leaf litter lignin fraction" ; + double fates_frag_seed_decay_rate(fates_pft) ; + fates_frag_seed_decay_rate:units = "yr-1" ; + fates_frag_seed_decay_rate:long_name = "fraction of seeds that decay per year" ; + double fates_grperc(fates_pft) ; + fates_grperc:units = "unitless" ; + fates_grperc:long_name = "Growth respiration factor" ; + double fates_hydro_avuln_gs(fates_pft) ; + fates_hydro_avuln_gs:units = "unitless" ; + fates_hydro_avuln_gs:long_name = "shape parameter for stomatal control of water vapor exiting leaf" ; + double fates_hydro_avuln_node(fates_hydr_organs, fates_pft) ; + fates_hydro_avuln_node:units = "unitless" ; + fates_hydro_avuln_node:long_name = "xylem vulnerability curve shape parameter" ; + double fates_hydro_epsil_node(fates_hydr_organs, fates_pft) ; + fates_hydro_epsil_node:units = "MPa" ; + fates_hydro_epsil_node:long_name = "bulk elastic modulus" ; + double fates_hydro_fcap_node(fates_hydr_organs, fates_pft) ; + fates_hydro_fcap_node:units = "unitless" ; + fates_hydro_fcap_node:long_name = "fraction of non-residual water that is capillary in source" ; + double fates_hydro_k_lwp(fates_pft) ; + fates_hydro_k_lwp:units = "unitless" ; + fates_hydro_k_lwp:long_name = "inner leaf humidity scaling coefficient" ; + double fates_hydro_kmax_node(fates_hydr_organs, fates_pft) ; + fates_hydro_kmax_node:units = "kg/MPa/m/s" ; + fates_hydro_kmax_node:long_name = "maximum xylem conductivity per unit conducting xylem area" ; + double fates_hydro_p50_gs(fates_pft) ; + fates_hydro_p50_gs:units = "MPa" ; + fates_hydro_p50_gs:long_name = "water potential at 50% loss of stomatal conductance" ; + double fates_hydro_p50_node(fates_hydr_organs, fates_pft) ; + fates_hydro_p50_node:units = "MPa" ; + fates_hydro_p50_node:long_name = "xylem water potential at 50% loss of conductivity" ; + double fates_hydro_p_taper(fates_pft) ; + fates_hydro_p_taper:units = "unitless" ; + fates_hydro_p_taper:long_name = "xylem taper exponent" ; + double fates_hydro_pinot_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pinot_node:units = "MPa" ; + fates_hydro_pinot_node:long_name = "osmotic potential at full turgor" ; + double fates_hydro_pitlp_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pitlp_node:units = "MPa" ; + fates_hydro_pitlp_node:long_name = "turgor loss point" ; + double fates_hydro_resid_node(fates_hydr_organs, fates_pft) ; + fates_hydro_resid_node:units = "cm3/cm3" ; + fates_hydro_resid_node:long_name = "residual water conent" ; + double fates_hydro_rfrac_stem(fates_pft) ; + fates_hydro_rfrac_stem:units = "fraction" ; + fates_hydro_rfrac_stem:long_name = "fraction of total tree resistance from troot to canopy" ; + double fates_hydro_rs2(fates_pft) ; + fates_hydro_rs2:units = "m" ; + fates_hydro_rs2:long_name = "absorbing root radius" ; + double fates_hydro_srl(fates_pft) ; + fates_hydro_srl:units = "m g-1" ; + fates_hydro_srl:long_name = "specific root length" ; + double fates_hydro_thetas_node(fates_hydr_organs, fates_pft) ; + fates_hydro_thetas_node:units = "cm3/cm3" ; + fates_hydro_thetas_node:long_name = "saturated water content" ; + double fates_hydro_vg_alpha_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_alpha_node:units = "MPa-1" ; + fates_hydro_vg_alpha_node:long_name = "(used if hydr_htftype_node = 2), capillary length parameter in van Genuchten model" ; + double fates_hydro_vg_m_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_m_node:units = "unitless" ; + fates_hydro_vg_m_node:long_name = "(used if hydr_htftype_node = 2),m in van Genuchten 1980 model, 2nd pore size distribution parameter" ; + double fates_hydro_vg_n_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_n_node:units = "unitless" ; + fates_hydro_vg_n_node:long_name = "(used if hydr_htftype_node = 2),n in van Genuchten 1980 model, pore size distribution parameter" ; + double fates_landuse_grazing_palatability(fates_pft) ; + fates_landuse_grazing_palatability:units = "unitless 0-1" ; + fates_landuse_grazing_palatability:long_name = "Relative intensity of leaf grazing/browsing per PFT" ; + double fates_landuse_harvest_pprod10(fates_pft) ; + fates_landuse_harvest_pprod10:units = "fraction" ; + fates_landuse_harvest_pprod10:long_name = "fraction of harvest wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_landuse_luc_frac_burned(fates_pft) ; + fates_landuse_luc_frac_burned:units = "fraction" ; + fates_landuse_luc_frac_burned:long_name = "fraction of land use change-generated and not-exported material that is burned (the remainder goes to litter)" ; + double fates_landuse_luc_frac_exported(fates_pft) ; + fates_landuse_luc_frac_exported:units = "fraction" ; + fates_landuse_luc_frac_exported:long_name = "fraction of land use change-generated wood material that is exported to wood product (the remainder is either burned or goes to litter)" ; + double fates_landuse_luc_pprod10(fates_pft) ; + fates_landuse_luc_pprod10:units = "fraction" ; + fates_landuse_luc_pprod10:long_name = "fraction of land use change wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_leaf_agross_btran_model(fates_pft) ; + fates_leaf_agross_btran_model:units = "index" ; + fates_leaf_agross_btran_model:long_name = "model switch for how gross assimilation affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_c3psn(fates_pft) ; + fates_leaf_c3psn:units = "flag" ; + fates_leaf_c3psn:long_name = "Photosynthetic pathway (1=c3, 0=c4)" ; + double fates_leaf_fnps(fates_pft) ; + fates_leaf_fnps:units = "fraction" ; + fates_leaf_fnps:long_name = "fraction of light absorbed by non-photosynthetic pigments" ; + double fates_leaf_jmaxha(fates_pft) ; + fates_leaf_jmaxha:units = "J/mol" ; + fates_leaf_jmaxha:long_name = "activation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxhd(fates_pft) ; + fates_leaf_jmaxhd:units = "J/mol" ; + fates_leaf_jmaxhd:long_name = "deactivation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxse(fates_pft) ; + fates_leaf_jmaxse:units = "J/mol/K" ; + fates_leaf_jmaxse:long_name = "entropy term for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_slamax(fates_pft) ; + fates_leaf_slamax:units = "m^2/gC" ; + fates_leaf_slamax:long_name = "Maximum Specific Leaf Area (SLA), even if under a dense canopy" ; + double fates_leaf_slatop(fates_pft) ; + fates_leaf_slatop:units = "m^2/gC" ; + fates_leaf_slatop:long_name = "Specific Leaf Area (SLA) at top of canopy, projected area basis" ; + double fates_leaf_stomatal_btran_model(fates_pft) ; + fates_leaf_stomatal_btran_model:units = "index" ; + fates_leaf_stomatal_btran_model:long_name = "model switch for how btran affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_stomatal_intercept(fates_pft) ; + fates_leaf_stomatal_intercept:units = "umol H2O/m**2/s" ; + fates_leaf_stomatal_intercept:long_name = "Minimum unstressed stomatal conductance for Ball-Berry model and Medlyn model" ; + double fates_leaf_stomatal_slope_ballberry(fates_pft) ; + fates_leaf_stomatal_slope_ballberry:units = "unitless" ; + fates_leaf_stomatal_slope_ballberry:long_name = "stomatal slope parameter, as per Ball-Berry" ; + double fates_leaf_stomatal_slope_medlyn(fates_pft) ; + fates_leaf_stomatal_slope_medlyn:units = "KPa**0.5" ; + fates_leaf_stomatal_slope_medlyn:long_name = "stomatal slope parameter, as per Medlyn" ; + double fates_leaf_vcmax25top(fates_leafage_class, fates_pft) ; + fates_leaf_vcmax25top:units = "umol CO2/m^2/s" ; + fates_leaf_vcmax25top:long_name = "maximum carboxylation rate of Rub. at 25C, canopy top" ; + double fates_leaf_vcmaxha(fates_pft) ; + fates_leaf_vcmaxha:units = "J/mol" ; + fates_leaf_vcmaxha:long_name = "activation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxhd(fates_pft) ; + fates_leaf_vcmaxhd:units = "J/mol" ; + fates_leaf_vcmaxhd:long_name = "deactivation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxse(fates_pft) ; + fates_leaf_vcmaxse:units = "J/mol/K" ; + fates_leaf_vcmaxse:long_name = "entropy term for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leafn_vert_scaler_coeff1(fates_pft) ; + fates_leafn_vert_scaler_coeff1:units = "unitless" ; + fates_leafn_vert_scaler_coeff1:long_name = "Coefficient one for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_leafn_vert_scaler_coeff2(fates_pft) ; + fates_leafn_vert_scaler_coeff2:units = "unitless" ; + fates_leafn_vert_scaler_coeff2:long_name = "Coefficient two for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_maintresp_leaf_atkin2017_baserate(fates_pft) ; + fates_maintresp_leaf_atkin2017_baserate:units = "umol CO2/m^2/s" ; + fates_maintresp_leaf_atkin2017_baserate:long_name = "Leaf maintenance respiration base rate parameter (r0) per Atkin et al 2017" ; + double fates_maintresp_leaf_ryan1991_baserate(fates_pft) ; + fates_maintresp_leaf_ryan1991_baserate:units = "gC/gN/s" ; + fates_maintresp_leaf_ryan1991_baserate:long_name = "Leaf maintenance respiration base rate per Ryan et al 1991" ; + double fates_maintresp_leaf_vert_scaler_coeff1(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff1:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff1:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff1." ; + double fates_maintresp_leaf_vert_scaler_coeff2(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff2:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff2:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff2." ; + double fates_maintresp_reduction_curvature(fates_pft) ; + fates_maintresp_reduction_curvature:units = "unitless (0-1)" ; + fates_maintresp_reduction_curvature:long_name = "curvature of MR reduction as f(carbon storage), 1=linear, 0=very curved" ; + double fates_maintresp_reduction_intercept(fates_pft) ; + fates_maintresp_reduction_intercept:units = "unitless (0-1)" ; + fates_maintresp_reduction_intercept:long_name = "intercept of MR reduction as f(carbon storage), 0=no throttling, 1=max throttling" ; + double fates_maintresp_reduction_upthresh(fates_pft) ; + fates_maintresp_reduction_upthresh:units = "unitless (0-1)" ; + fates_maintresp_reduction_upthresh:long_name = "upper threshold for storage biomass (relative to leaf biomass) above which MR is not reduced" ; + double fates_mort_bmort(fates_pft) ; + fates_mort_bmort:units = "1/yr" ; + fates_mort_bmort:long_name = "background mortality rate" ; + 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_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. " ; + double fates_mort_ip_size_senescence(fates_pft) ; + fates_mort_ip_size_senescence:units = "dbh cm" ; + fates_mort_ip_size_senescence:long_name = "Mortality dbh senescence inflection point. If _ this mortality term is off. Setting this value turns on size dependent mortality" ; + double fates_mort_prescribed_canopy(fates_pft) ; + fates_mort_prescribed_canopy:units = "1/yr" ; + fates_mort_prescribed_canopy:long_name = "mortality rate of canopy trees for prescribed physiology mode" ; + double fates_mort_prescribed_understory(fates_pft) ; + fates_mort_prescribed_understory:units = "1/yr" ; + fates_mort_prescribed_understory:long_name = "mortality rate of understory trees for prescribed physiology mode" ; + double fates_mort_r_age_senescence(fates_pft) ; + fates_mort_r_age_senescence:units = "mortality rate year^-1" ; + fates_mort_r_age_senescence:long_name = "Mortality age senescence rate of change. Sensible range is around 0.03-0.06. Larger values givesteeper mortality curves." ; + double fates_mort_r_size_senescence(fates_pft) ; + fates_mort_r_size_senescence:units = "mortality rate dbh^-1" ; + fates_mort_r_size_senescence:long_name = "Mortality dbh senescence rate of change. Sensible range is around 0.03-0.06. Larger values give steeper mortality curves." ; + double fates_mort_scalar_coldstress(fates_pft) ; + fates_mort_scalar_coldstress:units = "1/yr" ; + fates_mort_scalar_coldstress:long_name = "maximum mortality rate from cold stress" ; + double fates_mort_scalar_cstarvation(fates_pft) ; + fates_mort_scalar_cstarvation:units = "1/yr" ; + fates_mort_scalar_cstarvation:long_name = "maximum mortality rate from carbon starvation" ; + double fates_mort_scalar_hydrfailure(fates_pft) ; + fates_mort_scalar_hydrfailure:units = "1/yr" ; + fates_mort_scalar_hydrfailure:long_name = "maximum mortality rate from hydraulic failure" ; + double fates_mort_upthresh_cstarvation(fates_pft) ; + fates_mort_upthresh_cstarvation:units = "unitless" ; + fates_mort_upthresh_cstarvation:long_name = "threshold for storage biomass (relative to target leaf biomass) above which carbon starvation is zero" ; + double fates_nonhydro_smpsc(fates_pft) ; + fates_nonhydro_smpsc:units = "mm" ; + fates_nonhydro_smpsc:long_name = "Soil water potential at full stomatal closure" ; + double fates_nonhydro_smpso(fates_pft) ; + fates_nonhydro_smpso:units = "mm" ; + fates_nonhydro_smpso:long_name = "Soil water potential at full stomatal opening" ; + double fates_phen_cold_size_threshold(fates_pft) ; + fates_phen_cold_size_threshold:units = "cm" ; + fates_phen_cold_size_threshold:long_name = "the dbh size above which will lead to phenology-related stem and leaf drop" ; + double fates_phen_drought_threshold(fates_pft) ; + fates_phen_drought_threshold:units = "m3/m3 or mm" ; + fates_phen_drought_threshold:long_name = "threshold for drought phenology (or lower threshold for semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_flush_fraction(fates_pft) ; + fates_phen_flush_fraction:units = "fraction" ; + fates_phen_flush_fraction:long_name = "Upon bud-burst, the maximum fraction of storage carbon used for flushing leaves" ; + double fates_phen_fnrt_drop_fraction(fates_pft) ; + fates_phen_fnrt_drop_fraction:units = "fraction" ; + fates_phen_fnrt_drop_fraction:long_name = "fraction of fine roots to drop during drought/cold" ; + double fates_phen_leaf_habit(fates_pft) ; + fates_phen_leaf_habit:units = "flag" ; + fates_phen_leaf_habit:long_name = "Flag for leaf phenology habit. 1 - evergreen; 2 - season (cold) deciduous; 3 - stress (hydro) deciduous; 4 - stress (hydro) semi-deciduous" ; + double fates_phen_mindaysoff(fates_pft) ; + fates_phen_mindaysoff:units = "days" ; + fates_phen_mindaysoff:long_name = "day threshold compared against days since leaves abscised (shed)" ; + double fates_phen_moist_threshold(fates_pft) ; + fates_phen_moist_threshold:units = "m3/m3 or mm" ; + fates_phen_moist_threshold:long_name = "upper threshold for drought phenology (only for drought semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_stem_drop_fraction(fates_pft) ; + fates_phen_stem_drop_fraction:units = "fraction" ; + fates_phen_stem_drop_fraction:long_name = "fraction of stems to drop for non-woody species during drought/cold" ; + double fates_prescribed_npp_canopy(fates_pft) ; + fates_prescribed_npp_canopy:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_canopy:long_name = "NPP per unit crown area of canopy trees for prescribed physiology mode" ; + double fates_prescribed_npp_understory(fates_pft) ; + fates_prescribed_npp_understory:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_understory:long_name = "NPP per unit crown area of understory trees for prescribed physiology mode" ; + double fates_rad_leaf_clumping_index(fates_pft) ; + fates_rad_leaf_clumping_index:units = "fraction (0-1)" ; + fates_rad_leaf_clumping_index:long_name = "factor describing how much self-occlusion of leaf scattering elements decreases light interception" ; + double fates_rad_leaf_rhonir(fates_pft) ; + fates_rad_leaf_rhonir:units = "fraction" ; + fates_rad_leaf_rhonir:long_name = "Leaf reflectance: near-IR" ; + double fates_rad_leaf_rhovis(fates_pft) ; + fates_rad_leaf_rhovis:units = "fraction" ; + fates_rad_leaf_rhovis:long_name = "Leaf reflectance: visible" ; + double fates_rad_leaf_taunir(fates_pft) ; + fates_rad_leaf_taunir:units = "fraction" ; + fates_rad_leaf_taunir:long_name = "Leaf transmittance: near-IR" ; + double fates_rad_leaf_tauvis(fates_pft) ; + fates_rad_leaf_tauvis:units = "fraction" ; + fates_rad_leaf_tauvis:long_name = "Leaf transmittance: visible" ; + double fates_rad_leaf_xl(fates_pft) ; + fates_rad_leaf_xl:units = "unitless" ; + fates_rad_leaf_xl:long_name = "Leaf/stem orientation index" ; + double fates_rad_stem_rhonir(fates_pft) ; + fates_rad_stem_rhonir:units = "fraction" ; + fates_rad_stem_rhonir:long_name = "Stem reflectance: near-IR" ; + double fates_rad_stem_rhovis(fates_pft) ; + fates_rad_stem_rhovis:units = "fraction" ; + fates_rad_stem_rhovis:long_name = "Stem reflectance: visible" ; + double fates_rad_stem_taunir(fates_pft) ; + fates_rad_stem_taunir:units = "fraction" ; + fates_rad_stem_taunir:long_name = "Stem transmittance: near-IR" ; + double fates_rad_stem_tauvis(fates_pft) ; + fates_rad_stem_tauvis:units = "fraction" ; + fates_rad_stem_tauvis:long_name = "Stem transmittance: visible" ; + double fates_recruit_height_min(fates_pft) ; + fates_recruit_height_min:units = "m" ; + fates_recruit_height_min:long_name = "the minimum height (ie starting height) of a newly recruited plant" ; + double fates_recruit_init_density(fates_pft) ; + fates_recruit_init_density:units = "stems/m2" ; + fates_recruit_init_density:long_name = "initial seedling density for a cold-start near-bare-ground simulation. If negative sets initial tree dbh - only to be used in nocomp mode" ; + double fates_recruit_prescribed_rate(fates_pft) ; + fates_recruit_prescribed_rate:units = "n/yr" ; + fates_recruit_prescribed_rate:long_name = "recruitment rate for prescribed physiology mode" ; + double fates_recruit_seed_alloc(fates_pft) ; + fates_recruit_seed_alloc:units = "fraction" ; + fates_recruit_seed_alloc:long_name = "fraction of available carbon balance allocated to seeds" ; + double fates_recruit_seed_alloc_mature(fates_pft) ; + fates_recruit_seed_alloc_mature:units = "fraction" ; + fates_recruit_seed_alloc_mature:long_name = "fraction of available carbon balance allocated to seeds in mature plants (adds to fates_seed_alloc)" ; + double fates_recruit_seed_dbh_repro_threshold(fates_pft) ; + fates_recruit_seed_dbh_repro_threshold:units = "cm" ; + fates_recruit_seed_dbh_repro_threshold:long_name = "the diameter where the plant will increase allocation to the seed pool by fraction: fates_recruit_seed_alloc_mature" ; + double fates_recruit_seed_germination_rate(fates_pft) ; + fates_recruit_seed_germination_rate:units = "yr-1" ; + fates_recruit_seed_germination_rate:long_name = "fraction of seeds that germinate per year" ; + double fates_recruit_seed_supplement(fates_pft) ; + fates_recruit_seed_supplement:units = "KgC/m2/yr" ; + fates_recruit_seed_supplement:long_name = "Supplemental external seed rain source term (non-mass conserving)" ; + double fates_seed_dispersal_fraction(fates_pft) ; + fates_seed_dispersal_fraction:units = "fraction" ; + fates_seed_dispersal_fraction:long_name = "fraction of seed rain to be dispersed to other grid cells" ; + double fates_seed_dispersal_max_dist(fates_pft) ; + fates_seed_dispersal_max_dist:units = "m" ; + fates_seed_dispersal_max_dist:long_name = "maximum seed dispersal distance for a given pft" ; + double fates_seed_dispersal_pdf_scale(fates_pft) ; + fates_seed_dispersal_pdf_scale:units = "unitless" ; + fates_seed_dispersal_pdf_scale:long_name = "seed dispersal probability density function scale parameter, A, Table 1 Bullock et al 2016" ; + double fates_seed_dispersal_pdf_shape(fates_pft) ; + fates_seed_dispersal_pdf_shape:units = "unitless" ; + fates_seed_dispersal_pdf_shape:long_name = "seed dispersal probability density function shape parameter, B, Table 1 Bullock et al 2016" ; + double fates_stoich_nitr(fates_plant_organs, fates_pft) ; + fates_stoich_nitr:units = "gN/gC" ; + fates_stoich_nitr:long_name = "target nitrogen concentration (ratio with carbon) of organs" ; + double fates_stoich_phos(fates_plant_organs, fates_pft) ; + fates_stoich_phos:units = "gP/gC" ; + fates_stoich_phos:long_name = "target phosphorus concentration (ratio with carbon) of organs" ; + double fates_trim_inc(fates_pft) ; + fates_trim_inc:units = "m2/m2" ; + fates_trim_inc:long_name = "Arbitrary incremental change in trimming function." ; + double fates_trim_limit(fates_pft) ; + fates_trim_limit:units = "m2/m2" ; + fates_trim_limit:long_name = "Arbitrary limit to reductions in leaf area with stress" ; + double fates_trs_repro_alloc_a(fates_pft) ; + fates_trs_repro_alloc_a:units = "fraction" ; + fates_trs_repro_alloc_a:long_name = "shape parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_alloc_b(fates_pft) ; + fates_trs_repro_alloc_b:units = "fraction" ; + fates_trs_repro_alloc_b:long_name = "intercept parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_frac_seed(fates_pft) ; + fates_trs_repro_frac_seed:units = "fraction" ; + fates_trs_repro_frac_seed:long_name = "fraction of reproductive mass that is seed" ; + double fates_trs_seedling_a_emerg(fates_pft) ; + fates_trs_seedling_a_emerg:units = "day -1" ; + fates_trs_seedling_a_emerg:long_name = "mean fraction of seed bank emerging" ; + double fates_trs_seedling_b_emerg(fates_pft) ; + fates_trs_seedling_b_emerg:units = "day -1" ; + fates_trs_seedling_b_emerg:long_name = "seedling emergence sensitivity to soil moisture" ; + double fates_trs_seedling_background_mort(fates_pft) ; + fates_trs_seedling_background_mort:units = "yr-1" ; + fates_trs_seedling_background_mort:long_name = "background seedling mortality rate" ; + double fates_trs_seedling_h2o_mort_a(fates_pft) ; + fates_trs_seedling_h2o_mort_a:units = "-" ; + fates_trs_seedling_h2o_mort_a:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_b(fates_pft) ; + fates_trs_seedling_h2o_mort_b:units = "-" ; + fates_trs_seedling_h2o_mort_b:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_c(fates_pft) ; + fates_trs_seedling_h2o_mort_c:units = "-" ; + fates_trs_seedling_h2o_mort_c:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_light_mort_a(fates_pft) ; + fates_trs_seedling_light_mort_a:units = "-" ; + fates_trs_seedling_light_mort_a:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_mort_b(fates_pft) ; + fates_trs_seedling_light_mort_b:units = "-" ; + fates_trs_seedling_light_mort_b:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_rec_a(fates_pft) ; + fates_trs_seedling_light_rec_a:units = "-" ; + fates_trs_seedling_light_rec_a:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_light_rec_b(fates_pft) ; + fates_trs_seedling_light_rec_b:units = "-" ; + fates_trs_seedling_light_rec_b:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_mdd_crit(fates_pft) ; + fates_trs_seedling_mdd_crit:units = "mm H2O day" ; + fates_trs_seedling_mdd_crit:long_name = "critical moisture deficit (suction) day accumulation for seedling moisture-based seedling mortality to begin" ; + double fates_trs_seedling_par_crit_germ(fates_pft) ; + fates_trs_seedling_par_crit_germ:units = "MJ m-2 day-1" ; + fates_trs_seedling_par_crit_germ:long_name = "critical light level for germination" ; + double fates_trs_seedling_psi_crit(fates_pft) ; + fates_trs_seedling_psi_crit:units = "mm H2O" ; + fates_trs_seedling_psi_crit:long_name = "critical soil moisture (suction) for seedling stress" ; + double fates_trs_seedling_psi_emerg(fates_pft) ; + fates_trs_seedling_psi_emerg:units = "mm h20 suction" ; + fates_trs_seedling_psi_emerg:long_name = "critical soil moisture for seedling emergence" ; + double fates_trs_seedling_root_depth(fates_pft) ; + fates_trs_seedling_root_depth:units = "m" ; + fates_trs_seedling_root_depth:long_name = "rooting depth of seedlings" ; + double fates_turb_displar(fates_pft) ; + fates_turb_displar:units = "unitless" ; + fates_turb_displar:long_name = "Ratio of displacement height to canopy top height" ; + double fates_turb_leaf_diameter(fates_pft) ; + fates_turb_leaf_diameter:units = "m" ; + fates_turb_leaf_diameter:long_name = "Characteristic leaf dimension" ; + double fates_turb_z0mr(fates_pft) ; + fates_turb_z0mr:units = "unitless" ; + fates_turb_z0mr:long_name = "Ratio of momentum roughness length to canopy top height" ; + double fates_turnover_branch(fates_pft) ; + fates_turnover_branch:units = "yr" ; + fates_turnover_branch:long_name = "turnover time of branches" ; + double fates_turnover_fnrt(fates_pft) ; + fates_turnover_fnrt:units = "yr" ; + fates_turnover_fnrt:long_name = "root longevity (alternatively, turnover time)" ; + double fates_turnover_leaf_canopy(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_canopy:units = "yr" ; + fates_turnover_leaf_canopy:long_name = "Leaf longevity (ie turnover timescale) of canopy plants. For drought-deciduous PFTs, this also indicates the maximum length of the growing (i.e., leaves on) season." ; + double fates_turnover_leaf_ustory(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_ustory:units = "yr" ; + fates_turnover_leaf_ustory:long_name = "Leaf longevity (ie turnover timescale) of understory plants." ; + double fates_turnover_senleaf_fdrought(fates_pft) ; + fates_turnover_senleaf_fdrought:units = "unitless[0-1]" ; + fates_turnover_senleaf_fdrought:long_name = "multiplication factor for leaf longevity of senescent leaves during drought" ; + double fates_wood_density(fates_pft) ; + fates_wood_density:units = "g/cm3" ; + fates_wood_density:long_name = "mean density of woody tissue in plant" ; + double fates_woody(fates_pft) ; + fates_woody:units = "logical flag" ; + fates_woody:long_name = "Binary woody lifeform flag" ; + double fates_hlm_pft_map(fates_hlm_pftno, fates_pft) ; + fates_hlm_pft_map:units = "area fraction" ; + fates_hlm_pft_map:long_name = "In fixed biogeog mode, fraction of HLM area associated with each FATES PFT" ; + double fates_fire_FBD(fates_litterclass) ; + fates_fire_FBD:units = "kg Biomass/m3" ; + fates_fire_FBD:long_name = "fuel bulk density" ; + double fates_fire_low_moisture_Coeff(fates_litterclass) ; + fates_fire_low_moisture_Coeff:units = "NA" ; + fates_fire_low_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_low_moisture_Slope(fates_litterclass) ; + fates_fire_low_moisture_Slope:units = "NA" ; + fates_fire_low_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture(fates_litterclass) ; + fates_fire_mid_moisture:units = "NA" ; + fates_fire_mid_moisture:long_name = "spitfire litter moisture threshold to be considered medium dry" ; + double fates_fire_mid_moisture_Coeff(fates_litterclass) ; + fates_fire_mid_moisture_Coeff:units = "NA" ; + fates_fire_mid_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture_Slope(fates_litterclass) ; + fates_fire_mid_moisture_Slope:units = "NA" ; + fates_fire_mid_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_min_moisture(fates_litterclass) ; + fates_fire_min_moisture:units = "NA" ; + fates_fire_min_moisture:long_name = "spitfire litter moisture threshold to be considered very dry" ; + double fates_fire_SAV(fates_litterclass) ; + fates_fire_SAV:units = "cm-1" ; + fates_fire_SAV:long_name = "fuel surface area to volume ratio" ; + double fates_frag_maxdecomp(fates_litterclass) ; + fates_frag_maxdecomp:units = "yr-1" ; + fates_frag_maxdecomp:long_name = "maximum rate of litter & CWD transfer from non-decomposing class into decomposing class" ; + double fates_frag_cwd_frac(fates_NCWD) ; + fates_frag_cwd_frac:units = "fraction" ; + fates_frag_cwd_frac:long_name = "fraction of woody (bdead+bsw) biomass destined for CWD pool" ; + double fates_landuse_crop_lu_pft_vector(fates_landuseclass) ; + fates_landuse_crop_lu_pft_vector:units = "NA" ; + fates_landuse_crop_lu_pft_vector:long_name = "the FATES PFT index to use on a given crop land-use type (dummy value of -999 for non-crop types)" ; + double fates_landuse_grazing_rate(fates_landuseclass) ; + fates_landuse_grazing_rate:units = "1/day" ; + fates_landuse_grazing_rate:long_name = "fraction of leaf biomass consumed by grazers per day" ; + double fates_max_nocomp_pfts_by_landuse(fates_landuseclass) ; + fates_max_nocomp_pfts_by_landuse:units = "count" ; + fates_max_nocomp_pfts_by_landuse:long_name = "maximum number of nocomp PFTs on each land use type (only used in nocomp mode)" ; + double fates_maxpatches_by_landuse(fates_landuseclass) ; + fates_maxpatches_by_landuse:units = "count" ; + fates_maxpatches_by_landuse:long_name = "maximum number of patches per site on each land use type" ; + double fates_canopy_closure_thresh ; + fates_canopy_closure_thresh:units = "unitless" ; + fates_canopy_closure_thresh:long_name = "tree canopy coverage at which crown area allometry changes from savanna to forest value" ; + double fates_cnp_eca_plant_escalar ; + fates_cnp_eca_plant_escalar:units = "" ; + fates_cnp_eca_plant_escalar:long_name = "scaling factor for plant fine root biomass to calculate nutrient carrier enzyme abundance (ECA)" ; + double fates_cohort_age_fusion_tol ; + fates_cohort_age_fusion_tol:units = "unitless" ; + fates_cohort_age_fusion_tol:long_name = "minimum fraction in differece in cohort age between cohorts." ; + double fates_cohort_size_fusion_tol ; + fates_cohort_size_fusion_tol:units = "unitless" ; + fates_cohort_size_fusion_tol:long_name = "minimum fraction in difference in dbh between cohorts" ; + double fates_comp_excln ; + fates_comp_excln:units = "none" ; + fates_comp_excln:long_name = "IF POSITIVE: weighting factor (exponent on dbh) for canopy layer exclusion and promotion, IF NEGATIVE: switch to use deterministic height sorting" ; + double fates_damage_canopy_layer_code ; + fates_damage_canopy_layer_code:units = "unitless" ; + fates_damage_canopy_layer_code:long_name = "Integer code that decides whether damage affects canopy trees (1), understory trees (2)" ; + double fates_damage_event_code ; + fates_damage_event_code:units = "unitless" ; + fates_damage_event_code:long_name = "Integer code that options how damage events are structured" ; + double fates_dev_arbitrary ; + fates_dev_arbitrary:units = "unknown" ; + fates_dev_arbitrary:long_name = "Unassociated free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_active_crown_fire ; + fates_fire_active_crown_fire:units = "0 or 1" ; + fates_fire_active_crown_fire:long_name = "flag, 1=active crown fire 0=no active crown fire" ; + double fates_fire_cg_strikes ; + fates_fire_cg_strikes:units = "fraction (0-1)" ; + fates_fire_cg_strikes:long_name = "fraction of cloud to ground lightning strikes" ; + double fates_fire_drying_ratio ; + fates_fire_drying_ratio:units = "NA" ; + fates_fire_drying_ratio:long_name = "spitfire parameter, fire drying ratio for fuel moisture, alpha_FMC EQ 6 Thonicke et al 2010" ; + double fates_fire_durat_slope ; + fates_fire_durat_slope:units = "NA" ; + fates_fire_durat_slope:long_name = "spitfire parameter, fire max duration slope, Equation 14 Thonicke et al 2010" ; + double fates_fire_fdi_alpha ; + fates_fire_fdi_alpha:units = "NA" ; + fates_fire_fdi_alpha:long_name = "spitfire parameter, EQ 7 Venevsky et al. GCB 2002,(modified EQ 8 Thonicke et al. 2010) " ; + double fates_fire_fuel_energy ; + fates_fire_fuel_energy:units = "kJ/kg" ; + fates_fire_fuel_energy:long_name = "spitfire parameter, heat content of fuel" ; + double fates_fire_max_durat ; + fates_fire_max_durat:units = "minutes" ; + fates_fire_max_durat:long_name = "spitfire parameter, fire maximum duration, Equation 14 Thonicke et al 2010" ; + double fates_fire_miner_damp ; + fates_fire_miner_damp:units = "NA" ; + fates_fire_miner_damp:long_name = "spitfire parameter, mineral-dampening coefficient EQ A1 Thonicke et al 2010 " ; + double fates_fire_miner_total ; + fates_fire_miner_total:units = "fraction" ; + fates_fire_miner_total:long_name = "spitfire parameter, total mineral content, Table A1 Thonicke et al 2010" ; + double fates_fire_nignitions ; + fates_fire_nignitions:units = "ignitions per year per km2" ; + fates_fire_nignitions:long_name = "number of annual ignitions per square km" ; + double fates_fire_part_dens ; + fates_fire_part_dens:units = "kg/m2" ; + fates_fire_part_dens:long_name = "spitfire parameter, oven dry particle density, Table A1 Thonicke et al 2010" ; + double fates_fire_threshold ; + fates_fire_threshold:units = "kW/m" ; + fates_fire_threshold:long_name = "spitfire parameter, fire intensity threshold for tracking fires that spread" ; + double fates_frag_cwd_fcel ; + fates_frag_cwd_fcel:units = "unitless" ; + fates_frag_cwd_fcel:long_name = "Cellulose fraction for CWD" ; + double fates_frag_cwd_flig ; + fates_frag_cwd_flig:units = "unitless" ; + fates_frag_cwd_flig:long_name = "Lignin fraction of coarse woody debris" ; + double fates_hydro_kmax_rsurf1 ; + fates_hydro_kmax_rsurf1:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf1:long_name = "maximum conducitivity for unit root surface (into root)" ; + double fates_hydro_kmax_rsurf2 ; + fates_hydro_kmax_rsurf2:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf2:long_name = "maximum conducitivity for unit root surface (out of root)" ; + double fates_hydro_psi0 ; + fates_hydro_psi0:units = "MPa" ; + fates_hydro_psi0:long_name = "sapwood water potential at saturation" ; + double fates_hydro_psicap ; + fates_hydro_psicap:units = "MPa" ; + fates_hydro_psicap:long_name = "sapwood water potential at which capillary reserves exhausted" ; + double fates_landuse_grazing_carbon_use_eff ; + fates_landuse_grazing_carbon_use_eff:units = "unitless" ; + fates_landuse_grazing_carbon_use_eff:long_name = "carbon use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_maxheight ; + fates_landuse_grazing_maxheight:units = "m" ; + fates_landuse_grazing_maxheight:long_name = "maximum height that grazers (browsers, actually) can reach" ; + double fates_landuse_grazing_nitrogen_use_eff ; + fates_landuse_grazing_nitrogen_use_eff:units = "unitless" ; + fates_landuse_grazing_nitrogen_use_eff:long_name = "nitrogen use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_phosphorus_use_eff ; + fates_landuse_grazing_phosphorus_use_eff:units = "unitless" ; + fates_landuse_grazing_phosphorus_use_eff:long_name = "phosphorus use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_logging_coll_under_frac ; + fates_landuse_logging_coll_under_frac:units = "fraction" ; + fates_landuse_logging_coll_under_frac:long_name = "Fraction of stems killed in the understory when logging generates disturbance" ; + double fates_landuse_logging_collateral_frac ; + fates_landuse_logging_collateral_frac:units = "fraction" ; + fates_landuse_logging_collateral_frac:long_name = "Fraction of large stems in upperstory that die from logging collateral damage" ; + double fates_landuse_logging_dbhmax ; + fates_landuse_logging_dbhmax:units = "cm" ; + fates_landuse_logging_dbhmax:long_name = "Maximum dbh below which logging is applied (unset values flag this to be unused)" ; + double fates_landuse_logging_dbhmax_infra ; + fates_landuse_logging_dbhmax_infra:units = "cm" ; + fates_landuse_logging_dbhmax_infra:long_name = "Tree diameter, above which infrastructure from logging does not impact damage or mortality." ; + double fates_landuse_logging_dbhmin ; + fates_landuse_logging_dbhmin:units = "cm" ; + fates_landuse_logging_dbhmin:long_name = "Minimum dbh at which logging is applied" ; + double fates_landuse_logging_direct_frac ; + fates_landuse_logging_direct_frac:units = "fraction" ; + fates_landuse_logging_direct_frac:long_name = "Fraction of stems logged directly per event" ; + double fates_landuse_logging_event_code ; + fates_landuse_logging_event_code:units = "unitless" ; + fates_landuse_logging_event_code:long_name = "Integer code that options how logging events are structured" ; + double fates_landuse_logging_export_frac ; + fates_landuse_logging_export_frac:units = "fraction" ; + fates_landuse_logging_export_frac:long_name = "fraction of trunk product being shipped offsite, the leftovers will be left onsite as large CWD" ; + double fates_landuse_logging_mechanical_frac ; + fates_landuse_logging_mechanical_frac:units = "fraction" ; + fates_landuse_logging_mechanical_frac:long_name = "Fraction of stems killed due infrastructure an other mechanical means" ; + double fates_leaf_photo_temp_acclim_thome_time ; + fates_leaf_photo_temp_acclim_thome_time:units = "years" ; + fates_leaf_photo_temp_acclim_thome_time:long_name = "Length of the window for the long-term (i.e. T_home in Kumarathunge et al 2019) exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_photo_temp_acclim_timescale ; + fates_leaf_photo_temp_acclim_timescale:units = "days" ; + fates_leaf_photo_temp_acclim_timescale:long_name = "Length of the window for the exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_maintresp_leaf_model=2 or fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_theta_cj_c3 ; + fates_leaf_theta_cj_c3:units = "unitless" ; + fates_leaf_theta_cj_c3:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_leaf_theta_cj_c4 ; + fates_leaf_theta_cj_c4:units = "unitless" ; + fates_leaf_theta_cj_c4:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_maintresp_nonleaf_baserate ; + fates_maintresp_nonleaf_baserate:units = "gC/gN/s" ; + fates_maintresp_nonleaf_baserate:long_name = "Base maintenance respiration rate for plant tissues, using Ryan 1991" ; + double fates_maxcohort ; + fates_maxcohort:units = "count" ; + fates_maxcohort:long_name = "maximum number of cohorts per patch. Actual number of cohorts also depend on cohort fusion tolerances" ; + double fates_mort_disturb_frac ; + fates_mort_disturb_frac:units = "fraction" ; + fates_mort_disturb_frac:long_name = "fraction of canopy mortality that results in disturbance (i.e. transfer of area from old to new patch)" ; + double fates_mort_understorey_death ; + fates_mort_understorey_death:units = "fraction" ; + fates_mort_understorey_death:long_name = "fraction of plants in understorey cohort impacted by overstorey tree-fall" ; + double fates_patch_fusion_tol ; + fates_patch_fusion_tol:units = "unitless" ; + fates_patch_fusion_tol:long_name = "minimum fraction in difference in profiles between patches" ; + double fates_phen_chilltemp ; + fates_phen_chilltemp:units = "degrees C" ; + fates_phen_chilltemp:long_name = "chilling day counting threshold for vegetation" ; + double fates_phen_coldtemp ; + fates_phen_coldtemp:units = "degrees C" ; + fates_phen_coldtemp:long_name = "vegetation temperature exceedance that flags a cold-day for leaf-drop" ; + double fates_phen_gddthresh_a ; + fates_phen_gddthresh_a:units = "none" ; + fates_phen_gddthresh_a:long_name = "GDD accumulation function, intercept parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_b ; + fates_phen_gddthresh_b:units = "none" ; + fates_phen_gddthresh_b:long_name = "GDD accumulation function, multiplier parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_c ; + fates_phen_gddthresh_c:units = "none" ; + fates_phen_gddthresh_c:long_name = "GDD accumulation function, exponent parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_mindayson ; + fates_phen_mindayson:units = "days" ; + fates_phen_mindayson:long_name = "day threshold compared against days since leaves became on-allometry" ; + double fates_phen_ncolddayslim ; + fates_phen_ncolddayslim:units = "days" ; + fates_phen_ncolddayslim:long_name = "day threshold exceedance for temperature leaf-drop" ; + double fates_q10_froz ; + fates_q10_froz:units = "unitless" ; + fates_q10_froz:long_name = "Q10 for frozen-soil respiration rates" ; + double fates_q10_mr ; + fates_q10_mr:units = "unitless" ; + fates_q10_mr:long_name = "Q10 for maintenance respiration" ; + double fates_soil_salinity ; + fates_soil_salinity:units = "ppt" ; + fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; + double fates_trs_seedling2sap_par_timescale ; + fates_trs_seedling2sap_par_timescale:units = "days" ; + fates_trs_seedling2sap_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling to sapling transition rates" ; + double fates_trs_seedling_emerg_h2o_timescale ; + fates_trs_seedling_emerg_h2o_timescale:units = "days" ; + fates_trs_seedling_emerg_h2o_timescale:long_name = "Length of the window for the exponential moving average of smp used to calculate seedling emergence" ; + double fates_trs_seedling_mdd_timescale ; + fates_trs_seedling_mdd_timescale:units = "days" ; + fates_trs_seedling_mdd_timescale:long_name = "Length of the window for the exponential moving average of moisture deficit days used to calculate seedling mortality" ; + double fates_trs_seedling_mort_par_timescale ; + fates_trs_seedling_mort_par_timescale:units = "days" ; + fates_trs_seedling_mort_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling mortality" ; + double fates_vai_top_bin_width ; + fates_vai_top_bin_width:units = "m2/m2" ; + fates_vai_top_bin_width:long_name = "width in VAI units of uppermost leaf+stem layer scattering element in each canopy layer" ; + double fates_vai_width_increase_factor ; + fates_vai_width_increase_factor:units = "unitless" ; + fates_vai_width_increase_factor:long_name = "factor by which each leaf+stem scattering element increases in VAI width (1 = uniform spacing)" ; + +// global attributes: + :history = "This file was generated by BatchPatchParams.py:\nCDL Base File = fates_params_default.cdl\nXML patch file = archive/api36.1.0_100224_pr1255-2.xml" ; +data: + + fates_history_ageclass_bin_edges = 0, 1, 2, 5, 10, 20, 50 ; + + fates_history_coageclass_bin_edges = 0, 5 ; + + fates_history_height_bin_edges = 0, 0.1, 0.3, 1, 3, 10 ; + + fates_history_damage_bin_edges = 0, 80 ; + + fates_history_sizeclass_bin_edges = 0, 5, 10, 15, 20, 30, 40, 50, 60, 70, + 80, 90, 100 ; + + fates_alloc_organ_id = 1, 2, 3, 6 ; + + fates_hydro_htftype_node = 1, 1, 1, 1 ; + + fates_pftname = + "broadleaf_evergreen_tropical_tree ", + "needleleaf_evergreen_extratrop_tree ", + "needleleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_tree ", + "broadleaf_hydrodecid_tropical_tree ", + "broadleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_shrub ", + "broadleaf_hydrodecid_extratrop_shrub ", + "broadleaf_colddecid_extratrop_shrub ", + " broadleaf_evergreen_arctic_shrub ", + " broadleaf_colddecid_arctic_shrub ", + "arctic_c3_grass ", + "cool_c3_grass ", + "c4_grass " ; + + fates_hydro_organ_name = + "leaf ", + "stem ", + "transporting root ", + "absorbing root " ; + + fates_alloc_organ_name = + "leaf", + "fine root", + "sapwood", + "structure" ; + + fates_landuseclass_name = + "primaryland", + "secondaryland", + "rangeland", + "pastureland", + "cropland" ; + + fates_litterclass_name = + "twig ", + "small branch ", + "large branch ", + "trunk ", + "dead leaves ", + "live grass " ; + + fates_alloc_organ_priority = + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; + + fates_alloc_storage_cushion = 1.2, 1.2, 1.2, 1.2, 2.4, 1.2, 1.2, 2.4, 1.2, + 1.5, 1.4, 1.2, 1.2, 1.2 ; + + fates_alloc_store_priority_frac = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.7, 0.6, 0.6, 0.8, 0.8 ; + + fates_allom_agb1 = 0.0673, 0.1364012, 0.0393057, 0.2653695, 0.0673, + 0.0728698, 0.06896, 0.06896, 0.06896, 0.06896, 0.06896, 0.001, 0.001, + 0.003 ; + + fates_allom_agb2 = 0.976, 0.9449041, 1.087335, 0.8321321, 0.976, 1.0373211, + 0.572, 0.572, 0.572, 0.5289883, 0.6853945, 1.6592, 1.6592, 1.3456 ; + + fates_allom_agb3 = 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, + 2.1010352, 1.7628613, 1.248, 1.248, 1.869 ; + + fates_allom_agb4 = 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, + 0.931, 0.931, 0.931, -999.9, -999.9, -999.9 ; + + fates_allom_agb_frac = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 1, 1, 1 ; + + fates_allom_amode = 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_blca_expnt_diff = -0.12, -0.34, -0.32, -0.22, -0.12, -0.35, 0, + 0, 0, 0, 0, -0.487, -0.487, -0.259 ; + + fates_allom_cmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_d2bl1 = 0.04, 0.07, 0.07, 0.01, 0.04, 0.07, 0.07, 0.07, 0.07, + 0.0481934, 0.0481934, 0.0004, 0.0004, 0.0012 ; + + fates_allom_d2bl2 = 1.6019679, 1.5234373, 1.3051237, 1.9621397, 1.6019679, + 1.3998939, 1.3, 1.3, 1.3, 1.0600586, 1.7176758, 1.7092, 1.7092, 1.5879 ; + + fates_allom_d2bl3 = 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, + 0.55, 0.55, 0.3417, 0.3417, 0.9948 ; + + fates_allom_d2ca_coefficient_max = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2ca_coefficient_min = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2h1 = 78.4087704, 306.842667, 106.8745821, 104.3586841, + 78.4087704, 31.4557047, 0.64, 0.64, 0.64, 0.8165625, 0.778125, 0.1812, + 0.1812, 0.3353 ; + + fates_allom_d2h2 = 0.8124383, 0.752377, 0.9471302, 1.1146973, 0.8124383, + 0.9734088, 0.37, 0.37, 0.37, 0.2316113, 0.4027002, 0.6384, 0.6384, 0.4235 ; + + fates_allom_d2h3 = 47.6666164, 196.6865691, 93.9790461, 160.6835089, + 47.6666164, 16.5928174, -999.9, -999.9, -999.9, -999.9, -999.9, -999.9, + -999.9, -999.9 ; + + fates_allom_dbh_maxheight = 1000, 1000, 1000, 1000, 1000, 1000, 3, 3, 2, + 2.4, 1.9, 20, 20, 30 ; + + fates_allom_dmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fnrt_prof_a = 7, 7, 7, 7, 6, 6, 7, 7, 7, 7, 7, 11, 11, 11 ; + + fates_allom_fnrt_prof_b = 1, 2, 2, 1, 2, 2, 1.5, 1.5, 1.5, 1.5, 1.5, 2, 2, 2 ; + + fates_allom_fnrt_prof_mode = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 ; + + fates_allom_frbstor_repro = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_h2cd1 = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.95, 0.95, 0.95, 0.95, + 0.95, 1, 1, 1 ; + + fates_allom_h2cd2 = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_hmode = 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 3, 3, 3 ; + + fates_allom_l2fr = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.67, 0.67, 1.41 ; + + fates_allom_la_per_sa_int = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.8, 0.8, 0.8, 0.8 ; + + fates_allom_la_per_sa_slp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_lmode = 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_sai_scaler = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1 ; + + fates_allom_smode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 ; + + fates_allom_stmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_zroot_k = 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ; + + fates_allom_zroot_max_dbh = 100, 100, 100, 100, 100, 100, 2, 2, 2, 2, 2, 2, + 2, 2 ; + + fates_allom_zroot_max_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_allom_zroot_min_dbh = 1, 1, 1, 2.5, 2.5, 2.5, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_allom_zroot_min_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_c2b = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_cnp_eca_alpha_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_decompmicc = 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280 ; + + fates_cnp_eca_km_nh4 = 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, + 0.14, 0.14, 0.14, 0.14, 0.14 ; + + fates_cnp_eca_km_no3 = 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, + 0.27, 0.27, 0.27, 0.27, 0.27 ; + + fates_cnp_eca_km_p = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_eca_km_ptase = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_eca_lambda_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_vmax_ptase = 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, + 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09 ; + + fates_cnp_nfix1 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_nitr_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_phos_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_pid_kd = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_pid_ki = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_pid_kp = 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, + 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005 ; + + fates_cnp_prescribed_nuptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_prescribed_puptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_store_ovrflw_frac = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_turnover_nitr_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_turnover_phos_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_vmax_nh4 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_no3 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_p = 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, + 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10 ; + + fates_damage_frac = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_damage_mort_p1 = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 ; + + fates_damage_mort_p2 = 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.5 ; + + fates_damage_recovery_scalar = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_dev_arbitrary_pft = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_fire_alpha_SH = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2 ; + + fates_fire_bark_scaler = 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, + 0.07, 0.07, 0.07, 0.07, 0.07, 0.07 ; + + fates_fire_crown_kill = 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, + 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775 ; + + fates_frag_fnrt_fcel = 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_frag_fnrt_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_fnrt_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_fcel = 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_frag_leaf_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_seed_decay_rate = 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, + 0.51, 0.74, 0.46, 0.35, 0.51, 0.51 ; + + fates_grperc = 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.12, + 0.11, 0.16, 0.11, 0.11 ; + + fates_hydro_avuln_gs = 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, + 2.5, 2.5, 2.5, 2.5 ; + + fates_hydro_avuln_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_hydro_epsil_node = + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_hydro_fcap_node = + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_k_lwp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_kmax_node = + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999 ; + + fates_hydro_p50_gs = -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, + -1.5, -1.5, -1.5, -1.5, -1.5 ; + + fates_hydro_p50_node = + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25 ; + + fates_hydro_p_taper = 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333 ; + + fates_hydro_pinot_node = + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478 ; + + fates_hydro_pitlp_node = + -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, + -1.67, -1.67, -1.67, -1.67, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, + -1.2, -1.2 ; + + fates_hydro_resid_node = + 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, + 0.16, 0.16, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, + 0.11, 0.11 ; + + fates_hydro_rfrac_stem = 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625 ; + + fates_hydro_rs2 = 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, + 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001 ; + + fates_hydro_srl = 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25 ; + + fates_hydro_thetas_node = + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75 ; + + fates_hydro_vg_alpha_node = + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12 ; + + fates_hydro_vg_m_node = + 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, + 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, + 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, + 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_hydro_vg_n_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_landuse_grazing_palatability = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 ; + + fates_landuse_harvest_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, + 1, 1, 1 ; + + fates_landuse_luc_frac_burned = 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_landuse_luc_frac_exported = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.2, 0.2, + 0.2, 0.2, 0.2, 0, 0, 0 ; + + fates_landuse_luc_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, 1, + 1, 1 ; + + fates_leaf_agross_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_c3psn = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 ; + + fates_leaf_fnps = 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, + 0.15, 0.15, 0.15, 0.15, 0.15 ; + + fates_leaf_jmaxha = 43540, 43540, 43540, 43540, 43540, 43540, 43540, 43540, + 43540, 43540, 43540, 43540, 43540, 43540 ; + + fates_leaf_jmaxhd = 152040, 152040, 152040, 152040, 152040, 152040, 152040, + 152040, 152040, 152040, 152040, 152040, 152040, 152040 ; + + fates_leaf_jmaxse = 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495 ; + + fates_leaf_slamax = 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.012, + 0.03, 0.03, 0.012, 0.032, 0.05, 0.05, 0.05 ; + + fates_leaf_slatop = 0.012, 0.005, 0.024, 0.009, 0.03, 0.03, 0.012, 0.03, + 0.03, 0.01, 0.032, 0.027, 0.05, 0.05 ; + + fates_leaf_stomatal_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_stomatal_intercept = 10000, 10000, 10000, 10000, 10000, 10000, + 10000, 10000, 10000, 10000, 10000, 10000, 10000, 40000 ; + + fates_leaf_stomatal_slope_ballberry = 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_leaf_stomatal_slope_medlyn = 4.1, 2.3, 2.3, 4.1, 4.4, 4.4, 4.7, 4.7, + 4.7, 4.7, 4.7, 2.2, 5.3, 1.6 ; + + fates_leaf_vcmax25top = + 50, 62, 39, 61, 58, 58, 62, 54, 54, 38, 54, 86, 78, 78 ; + + fates_leaf_vcmaxha = 65330, 65330, 65330, 65330, 65330, 65330, 65330, 65330, + 65330, 65330, 65330, 65330, 65330, 65330 ; + + fates_leaf_vcmaxhd = 149250, 149250, 149250, 149250, 149250, 149250, 149250, + 149250, 149250, 149250, 149250, 149250, 149250, 149250 ; + + fates_leaf_vcmaxse = 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, + 485, 485, 485 ; + + fates_leafn_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963 ; + + fates_leafn_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_leaf_atkin2017_baserate = 1.756, 1.4995, 1.4995, 1.756, + 1.756, 1.756, 2.0749, 2.0749, 2.0749, 2.0749, 2.0749, 2.1956, 2.1956, + 2.1956 ; + + fates_maintresp_leaf_ryan1991_baserate = 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06 ; + + fates_maintresp_leaf_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963 ; + + fates_maintresp_leaf_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_reduction_curvature = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_maintresp_reduction_intercept = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_maintresp_reduction_upthresh = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_mort_bmort = 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, + 0.014, 0.016, 0.01, 0.014, 0.014, 0.014 ; + + 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, + 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 = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_prescribed_canopy = 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, + 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194 ; + + fates_mort_prescribed_understory = 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, + 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025 ; + + fates_mort_r_age_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_r_size_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_scalar_coldstress = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3.5, 2.3, 3, 3 ; + + fates_mort_scalar_cstarvation = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.57, 0.6, 0.6, 0.6 ; + + fates_mort_scalar_hydrfailure = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.8, 0.6, 0.6, 0.6 ; + + fates_mort_upthresh_cstarvation = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_nonhydro_smpsc = -255000, -255000, -255000, -255000, -255000, -255000, + -255000, -255000, -255000, -255000, -255000, -255000, -255000, -255000 ; + + fates_nonhydro_smpso = -66000, -66000, -66000, -66000, -66000, -66000, + -66000, -66000, -66000, -66000, -66000, -66000, -66000, -66000 ; + + fates_phen_cold_size_threshold = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_drought_threshold = -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4 ; + + fates_phen_flush_fraction = _, _, 0.5, _, 0.5, 0.5, _, 0.5, 0.5, _, 0.5, + 0.5, 0.5, 0.5 ; + + fates_phen_fnrt_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_leaf_habit = 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 2, 2, 3, 3 ; + + fates_phen_mindaysoff = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_phen_moist_threshold = -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9 ; + + fates_phen_stem_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_prescribed_npp_canopy = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4, 0.4, 0.4, 0.4 ; + + fates_prescribed_npp_understory = 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125 ; + + fates_rad_leaf_clumping_index = 0.85, 0.85, 0.8, 0.85, 0.85, 0.9, 0.85, 0.9, + 0.9, 0.85, 0.9, 0.75, 0.75, 0.75 ; + + fates_rad_leaf_rhonir = 0.46, 0.41, 0.39, 0.46, 0.41, 0.41, 0.46, 0.41, + 0.41, 0.46, 0.41, 0.28, 0.28, 0.28 ; + + fates_rad_leaf_rhovis = 0.11, 0.09, 0.08, 0.11, 0.08, 0.08, 0.11, 0.08, + 0.08, 0.11, 0.08, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_taunir = 0.33, 0.32, 0.42, 0.33, 0.43, 0.43, 0.33, 0.43, + 0.43, 0.33, 0.43, 0.4, 0.4, 0.4 ; + + fates_rad_leaf_tauvis = 0.06, 0.04, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_xl = 0.32, 0.01, 0.01, 0.32, 0.2, 0.59, 0.32, 0.59, 0.59, + 0.32, 0.59, -0.23, -0.23, -0.23 ; + + fates_rad_stem_rhonir = 0.49, 0.36, 0.36, 0.49, 0.49, 0.49, 0.49, 0.49, + 0.49, 0.49, 0.49, 0.53, 0.53, 0.53 ; + + fates_rad_stem_rhovis = 0.21, 0.12, 0.12, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, 0.21, 0.31, 0.31, 0.31 ; + + fates_rad_stem_taunir = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.25, 0.25, 0.25 ; + + fates_rad_stem_tauvis = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.12, 0.12, 0.12 ; + + fates_recruit_height_min = 1.3, 1.3, 1.3, 1.3, 1.3, 1.3, 0.2, 0.2, 0.2, 0.8, + 0.8, 0.11, 0.2, 0.2 ; + + fates_recruit_init_density = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.16, 0.2, 0.2, 0.2, 0.2 ; + + fates_recruit_prescribed_rate = 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02 ; + + fates_recruit_seed_alloc = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.07, 0.1, 0, 0, 0 ; + + fates_recruit_seed_alloc_mature = 0, 0, 0, 0, 0, 0, 0.9, 0.9, 0.9, 0.9, 0.9, + 0.25, 0.25, 0.2 ; + + fates_recruit_seed_dbh_repro_threshold = 90, 80, 80, 80, 90, 80, 3, 3, 2, + 2.4, 1.9, 3, 3, 3 ; + + fates_recruit_seed_germination_rate = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.4, 0.49, 0.29, 0.5, 0.5 ; + + fates_recruit_seed_supplement = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_seed_dispersal_fraction = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_max_dist = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_scale = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_shape = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_stoich_nitr = + 0.033, 0.029, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, + 0.04, 0.04, 0.04, + 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, + 0.024, 0.024, 0.024, 0.024, + 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, + 1e-08, 1e-08, 1e-08, 1e-08, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047 ; + + fates_stoich_phos = + 0.0033, 0.0029, 0.004, 0.0033, 0.004, 0.004, 0.0033, 0.004, 0.004, 0.0033, + 0.004, 0.004, 0.004, 0.004, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, + 1e-09, 1e-09, 1e-09, 1e-09, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047 ; + + fates_trim_inc = 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, + 0.03, 0.03, 0.03, 0.03 ; + + fates_trim_limit = 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.3, 0.3, 0.3 ; + + fates_trs_repro_alloc_a = 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, + 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049 ; + + fates_trs_repro_alloc_b = -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171 ; + + fates_trs_repro_frac_seed = 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, + 0.24, 0.24, 0.24, 0.24, 0.24, 0.24 ; + + fates_trs_seedling_a_emerg = 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, + 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003 ; + + fates_trs_seedling_b_emerg = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.2, 1.2, 1.2 ; + + fates_trs_seedling_background_mort = 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371 ; + + fates_trs_seedling_h2o_mort_a = 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17 ; + + fates_trs_seedling_h2o_mort_b = -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11 ; + + fates_trs_seedling_h2o_mort_c = 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05 ; + + fates_trs_seedling_light_mort_a = -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694 ; + + fates_trs_seedling_light_mort_b = -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063 ; + + fates_trs_seedling_light_rec_a = 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, + 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007 ; + + fates_trs_seedling_light_rec_b = 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, + 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615 ; + + fates_trs_seedling_mdd_crit = 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000 ; + + fates_trs_seedling_par_crit_germ = 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, + 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656 ; + + fates_trs_seedling_psi_crit = -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7 ; + + fates_trs_seedling_psi_emerg = -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65 ; + + fates_trs_seedling_root_depth = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06 ; + + fates_turb_displar = 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, + 0.67, 0.67, 0.67, 0.67, 0.67 ; + + fates_turb_leaf_diameter = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + 0.04, 0.04, 0.04, 0.04, 0.04, 0.04 ; + + fates_turb_z0mr = 0.075, 0.055, 0.055, 0.075, 0.055, 0.055, 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12 ; + + fates_turnover_branch = 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 0, 0, 0 ; + + fates_turnover_fnrt = 1, 2, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_canopy = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_ustory = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_senleaf_fdrought = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_wood_density = 0.548327, 0.44235, 0.454845, 0.754336, 0.548327, + 0.566452, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7 ; + + fates_woody = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ; + + fates_hlm_pft_map = + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0.1, 0.8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ; + + fates_fire_FBD = 15.4, 16.8, 19.6, 999, 4, 4 ; + + fates_fire_low_moisture_Coeff = 1.12, 1.09, 0.98, 0.8, 1.15, 1.15 ; + + fates_fire_low_moisture_Slope = 0.62, 0.72, 0.85, 0.8, 0.62, 0.62 ; + + fates_fire_mid_moisture = 0.72, 0.51, 0.38, 1, 0.8, 0.8 ; + + fates_fire_mid_moisture_Coeff = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_mid_moisture_Slope = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_min_moisture = 0.18, 0.12, 0, 0, 0.24, 0.24 ; + + fates_fire_SAV = 13, 3.58, 0.98, 0.2, 66, 66 ; + + fates_frag_maxdecomp = 0.52, 0.383, 0.383, 0.19, 1, 999 ; + + fates_frag_cwd_frac = 0.045, 0.075, 0.21, 0.67 ; + + fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 11 ; + + fates_landuse_grazing_rate = 0, 0, 0, 0, 0 ; + + fates_max_nocomp_pfts_by_landuse = 4, 4, 1, 1, 1 ; + + fates_maxpatches_by_landuse = 9, 4, 1, 1, 1 ; + + fates_canopy_closure_thresh = 0.8 ; + + fates_cnp_eca_plant_escalar = 1.25e-05 ; + + fates_cohort_age_fusion_tol = 0.08 ; + + fates_cohort_size_fusion_tol = 0.08 ; + + fates_comp_excln = -1 ; + + fates_damage_canopy_layer_code = 1 ; + + fates_damage_event_code = 1 ; + + fates_dev_arbitrary = _ ; + + fates_fire_active_crown_fire = 0 ; + + fates_fire_cg_strikes = 0.2 ; + + fates_fire_drying_ratio = 66000 ; + + fates_fire_durat_slope = -11.06 ; + + fates_fire_fdi_alpha = 0.00037 ; + + fates_fire_fuel_energy = 18000 ; + + fates_fire_max_durat = 240 ; + + fates_fire_miner_damp = 0.41739 ; + + fates_fire_miner_total = 0.055 ; + + fates_fire_nignitions = 15 ; + + fates_fire_part_dens = 513 ; + + fates_fire_threshold = 50 ; + + fates_frag_cwd_fcel = 0.76 ; + + fates_frag_cwd_flig = 0.24 ; + + fates_hydro_kmax_rsurf1 = 20 ; + + fates_hydro_kmax_rsurf2 = 0.0001 ; + + fates_hydro_psi0 = 0 ; + + fates_hydro_psicap = -0.6 ; + + fates_landuse_grazing_carbon_use_eff = 0 ; + + fates_landuse_grazing_maxheight = 1 ; + + fates_landuse_grazing_nitrogen_use_eff = 0.25 ; + + fates_landuse_grazing_phosphorus_use_eff = 0.5 ; + + fates_landuse_logging_coll_under_frac = 0. ; + + fates_landuse_logging_collateral_frac = 0. ; + + fates_landuse_logging_dbhmax = _ ; + + fates_landuse_logging_dbhmax_infra = 0 ; + + fates_landuse_logging_dbhmin = 0 ; + + fates_landuse_logging_direct_frac = 1. ; + + fates_landuse_logging_event_code = -30 ; + + fates_landuse_logging_export_frac = 0.8 ; + + fates_landuse_logging_mechanical_frac = 0. ; + + fates_leaf_photo_temp_acclim_thome_time = 30 ; + + fates_leaf_photo_temp_acclim_timescale = 30 ; + + fates_leaf_theta_cj_c3 = 0.999 ; + + fates_leaf_theta_cj_c4 = 0.999 ; + + fates_maintresp_nonleaf_baserate = 2.525e-06 ; + + fates_maxcohort = 100 ; + + fates_mort_disturb_frac = 1 ; + + fates_mort_understorey_death = 0.55983 ; + + fates_patch_fusion_tol = 0.05 ; + + fates_phen_chilltemp = 5 ; + + fates_phen_coldtemp = 7.5 ; + + fates_phen_gddthresh_a = -68 ; + + fates_phen_gddthresh_b = 638 ; + + fates_phen_gddthresh_c = -0.01 ; + + fates_phen_mindayson = 90 ; + + fates_phen_ncolddayslim = 5 ; + + fates_q10_froz = 1.5 ; + + fates_q10_mr = 1.5 ; + + fates_soil_salinity = 0.4 ; + + fates_trs_seedling2sap_par_timescale = 32 ; + + fates_trs_seedling_emerg_h2o_timescale = 7 ; + + fates_trs_seedling_mdd_timescale = 126 ; + + fates_trs_seedling_mort_par_timescale = 32 ; + + fates_vai_top_bin_width = 1 ; + + fates_vai_width_increase_factor = 1 ; +} diff --git a/parameter_files/archive/api41.0.0_prxxx_patch_params.xml b/parameter_files/archive/api41.0.0_prxxx_patch_params.xml new file mode 100644 index 0000000000..c6fc218f7b --- /dev/null +++ b/parameter_files/archive/api41.0.0_prxxx_patch_params.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + archive/api40.0.0_060625_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + fates_leaf_theta_cj_c3 + + + fates_leaf_theta_cj_c4 + + + \ No newline at end of file From b9ab13a7a46bffce527681e31a1b39b4373fe3c7 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 9 Jun 2025 09:13:17 -0700 Subject: [PATCH 077/194] Changed burn-flux bc_out to use the already existing mass_balance variable burn_flux_to_atm --- biogeochem/EDPatchDynamicsMod.F90 | 37 ++++++++++++++++++------------- main/EDMainMod.F90 | 5 +++++ 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 75245b94a3..97008a8a50 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -1081,10 +1081,9 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! Add burned leaf carbon to the atmospheric carbon flux ! for burning. ! [frac/day]*[kgC/plant]*[plant/ha]*[m2/ha]*[day/s] = [kg/m2/s] - - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & - leaf_burn_frac * nc%prt%GetState(leaf_organ, carbon12_element) * & - nc%n * ha_per_m2 * days_per_sec + !bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & + ! leaf_burn_frac * nc%prt%GetState(leaf_organ, carbon12_element) * & + ! nc%n * ha_per_m2 * days_per_sec ! Here the mass is removed from the plant @@ -2003,7 +2002,9 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !if(element_list(el) == carbon12_element) then + ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !end if ! Transfer below ground CWD (none burns) @@ -2034,8 +2035,10 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - + !if(element_list(el) == carbon12_element) then + ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !end if + ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%root_fines(dcmpy,sl) * patch_site_areadis @@ -2247,7 +2250,9 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !if(element_list(el) == carbon12_element) then + ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !end if call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2310,7 +2315,10 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & burned_mass = num_dead_trees * SF_val_CWD_frac_adj(c) * bstem * & currentCohort%fraction_crown_burned site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + + !if(element_list(el) == carbon12_element) then + ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !end if endif new_litt%ag_cwd(c) = new_litt%ag_cwd(c) + donatable_mass * donate_m2 curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass * retain_m2 @@ -2320,7 +2328,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & currentCohort => currentCohort%taller enddo - end do + end do return end subroutine fire_litter_fluxes @@ -2401,7 +2409,7 @@ subroutine mortality_litter_fluxes(currentSite, currentPatch, & do el = 1,num_elements - + element_id = element_list(el) site_mass => currentSite%mass_balance(el) elflux_diags => currentSite%flux_diags%elem(el) @@ -2722,8 +2730,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & end do site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2784,7 +2791,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & EDPftvarcon_inst%landusechange_frac_burned(pft) site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec else ! all other pools can end up as timber products or burn or go to litter donatable_mass = donatable_mass * (1.0_r8-EDPftvarcon_inst%landusechange_frac_exported(pft)) * & (1.0_r8-EDPftvarcon_inst%landusechange_frac_burned(pft)) @@ -2798,7 +2805,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec trunk_product_site = trunk_product_site + & woodproduct_mass diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 15252d63c6..de0d956756 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -918,6 +918,11 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) bc_out%litter_cwd_c_si = bc_out%litter_cwd_c_si * g_per_kg * AREA_INV bc_out%seed_c_si = bc_out%seed_c_si * g_per_kg * AREA_INV + ! Set boundary condition to HLM for carbon loss to atm from fires + ! [kgC/ha/day]*[m2/ha]*[day/s] = [kg/m2/s] + site_mass => currentSite%mass_balance(element_pos(carbon12_element)) + bc_out%fire_closs_to_atm_si = site_mass%burn_flux_to_atm * ha_per_m2 * days_per_sec + end subroutine ed_update_site From a378068eb588549bdc39d90d3b36021a070183e1 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 9 Jun 2025 09:15:01 -0700 Subject: [PATCH 078/194] declared site_cmass pointer for burn flux tracking --- main/EDMainMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index de0d956756..c47220b328 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -842,6 +842,7 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch + type(site_massbal_type), pointer :: site_cmass real(r8) :: total_stock ! dummy variable for receiving from sitemassstock !----------------------------------------------------------------------- @@ -920,8 +921,8 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! Set boundary condition to HLM for carbon loss to atm from fires ! [kgC/ha/day]*[m2/ha]*[day/s] = [kg/m2/s] - site_mass => currentSite%mass_balance(element_pos(carbon12_element)) - bc_out%fire_closs_to_atm_si = site_mass%burn_flux_to_atm * ha_per_m2 * days_per_sec + site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) + bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec end subroutine ed_update_site From ce4f3354c52e1043092a3048ad32f43031cf54d5 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 9 Jun 2025 09:36:30 -0700 Subject: [PATCH 079/194] updated bcout via herbivory to use site_mass --- biogeochem/EDPhysiologyMod.F90 | 28 ++++------------------------ main/EDMainMod.F90 | 7 ++++--- main/FatesInterfaceMod.F90 | 7 +++++-- main/FatesInterfaceTypesMod.F90 | 23 +++++++++++++++++++---- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index a40d6ba16b..68f9ee243b 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -144,7 +144,8 @@ module EDPhysiologyMod use PRTInitParamsFatesMod, only : NewRecruitTotalStoichiometry use FatesInterfaceTypesMod, only : hlm_use_luh use FatesInterfaceTypesMod, only : hlm_regeneration_model - + + implicit none private @@ -155,8 +156,6 @@ module EDPhysiologyMod public :: calculate_SP_properties public :: recruitment public :: ZeroLitterFluxes - public :: ZeroBCOutCarbonFluxes - public :: ZeroAllocationRates public :: PreDisturbanceLitterFluxes public :: PreDisturbanceIntegrateLitter @@ -232,20 +231,6 @@ end subroutine ZeroLitterFluxes ! ===================================================================================== - subroutine ZeroBCOutCarbonFluxes (bc_out) - - ! !ARGUMENTS - type(bc_out_type), intent(inout) :: bc_out - - bc_out%grazing_closs_to_atm_si = 0._r8 - bc_out%fire_closs_to_atm_si = 0._r8 - bc_out%gpp_site = 0._r8 - bc_out%ar_site = 0._r8 - - end subroutine ZeroBCOutCarbonFluxes - - ! ===================================================================================== - subroutine ZeroAllocationRates( currentSite ) ! !ARGUMENTS @@ -496,7 +481,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in, bc_out ! Send fluxes from newly created litter into the litter pools ! This litter flux is from non-disturbance inducing mortality, as well ! as litter fluxes from live trees - call CWDInput(currentSite, currentPatch, litt,bc_in, bc_out) + call CWDInput(currentSite, currentPatch, litt,bc_in) ! Only calculate fragmentation flux over layers that are active ! (RGK-Mar2019) SHOULD WE MAX THIS AT 1? DONT HAVE TO @@ -2803,7 +2788,7 @@ end subroutine recruitment ! ====================================================================================== - subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) + subroutine CWDInput( currentSite, currentPatch, litt, bc_in) ! ! !DESCRIPTION: @@ -2823,7 +2808,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) type(fates_patch_type),intent(inout), target :: currentPatch type(litter_type),intent(inout),target :: litt type(bc_in_type),intent(in) :: bc_in - type(bc_out_type),intent(inout) :: bc_out ! ! !LOCAL VARIABLES: @@ -2977,10 +2961,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) site_mass%herbivory_flux_out + & leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n - bc_out%grazing_closs_to_atm_si = bc_out%grazing_closs_to_atm_si + & - leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n * & - ha_per_m2 * days_per_sec - ! Assumption: turnover from deadwood and sapwood are lumped together in CWD pool !update partitioning of stem wood (struct + sapw) to cwd based on cohort dbh diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index c47220b328..4a06d778d3 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -27,6 +27,7 @@ module EDMainMod use FatesInterfaceTypesMod , only : hlm_masterproc use FatesInterfaceTypesMod , only : numpft use FatesInterfaceTypesMod , only : hlm_use_nocomp + use FatesInterfaceTypesMod , only : ZeroBCOutCarbonFluxes use PRTGenericMod , only : prt_carbon_allom_hyp use PRTGenericMod , only : prt_cnp_flex_allom_hyp use PRTGenericMod , only : nitrogen_element @@ -46,7 +47,7 @@ module EDMainMod use EDPhysiologyMod , only : SeedUpdate use EDPhysiologyMod , only : ZeroAllocationRates use EDPhysiologyMod , only : ZeroLitterFluxes - use EDPhysiologyMod , only : ZeroBCOutCarbonFluxes + use EDPhysiologyMod , only : PreDisturbanceLitterFluxes use EDPhysiologyMod , only : PreDisturbanceIntegrateLitter use EDPhysiologyMod , only : UpdateRecruitL2FR @@ -919,11 +920,11 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) bc_out%litter_cwd_c_si = bc_out%litter_cwd_c_si * g_per_kg * AREA_INV bc_out%seed_c_si = bc_out%seed_c_si * g_per_kg * AREA_INV - ! Set boundary condition to HLM for carbon loss to atm from fires + ! Set boundary condition to HLM for carbon loss to atm from fires and grazing ! [kgC/ha/day]*[m2/ha]*[day/s] = [kg/m2/s] site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec - + bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * ha_per_m2 * days_per_sec end subroutine ed_update_site diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 4bc525e8d5..0e725ca9c9 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -371,8 +371,11 @@ subroutine zero_bcs(fates,s) end select ! carbon loss to atmosphere pathways - fates%bc_out(s)%grazing_closs_to_atm_si = 0.0_r8 - fates%bc_out(s)%fire_closs_to_atm_si = 0.0_r8 + ! (these values are a unit conversion off of the + ! equivalent "site_mass%" diagnostics, so they are not + ! incremented but set during update_site()) + fates%bc_out(s)%grazing_closs_to_atm_si = nan + fates%bc_out(s)%fire_closs_to_atm_si = nan fates%bc_out(s)%rssun_pa(:) = 0.0_r8 fates%bc_out(s)%rssha_pa(:) = 0.0_r8 diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 67323a40d4..dee0ec2cb3 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -858,10 +858,25 @@ module FatesInterfaceTypesMod ! increasing, or all 1s) end type bc_pconst_type - + + public :: ZeroBCOutCarbonFluxes + contains - ! ====================================================================================== - + ! ====================================================================================== + + subroutine ZeroBCOutCarbonFluxes(bc_out) + + ! !ARGUMENTS + type(bc_out_type), intent(inout) :: bc_out + + bc_out%grazing_closs_to_atm_si = nan ! set via site_mass%burn_flux + bc_out%fire_closs_to_atm_si = nan ! set via site_mass%herbivory_flux_out + bc_out%gpp_site = 0._r8 + bc_out%ar_site = 0._r8 + + end subroutine ZeroBCOutCarbonFluxes + + - end module FatesInterfaceTypesMod +end module FatesInterfaceTypesMod From 9778b2b9f55801fd901e85c8338c2e64aee0cb02 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 9 Jun 2025 15:38:27 -0700 Subject: [PATCH 080/194] minor reorder of calculation to highlight conversion --- biogeochem/EDPhysiologyMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 8684ab4d4e..739c10e209 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2176,7 +2176,7 @@ subroutine SeedUpdate( currentSite ) ! Seed input from local sources (within site). Note that a fraction of the ! internal seed rain is sent out to neighboring gridcells. litt%seed_in_local(pft) = litt%seed_in_local(pft) + nocomp_seed_scaling * & - site_seed_rain(pft)*(1.0_r8-site_disp_frac(pft))/area ![kg/m2/day] + (1.0_r8-site_disp_frac(pft)) * (site_seed_rain(pft)/area) ! site_seed_rain conversion from [kg/site/day -> kg/m2/day] ! If we are using the Tree Recruitment Scheme (TRS) with or w/o seedling dynamics if ( any(hlm_regeneration_model == [TRS_regeneration, TRS_no_seedling_dyn]) .and. & From affa7042a772cd8f62a757d4b6a96fad2ca6ebd1 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 9 Jun 2025 16:22:18 -0700 Subject: [PATCH 081/194] update external seed supply to work with seed localization switch --- biogeochem/EDPhysiologyMod.F90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index d395b6666e..12cab3db61 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2079,6 +2079,7 @@ subroutine SeedUpdate( currentSite ) logical, parameter :: nocomp_seed_localization = .true. ! if nocomp is on, only send a given PFT's seeds to patches of that nocomp PFT real(r8) :: nocomp_seed_scaling ! scalar to handle case for nocomp_seed_localization + real(r8) :: seed_supply ! external seed rain scalar to handle case for nocomp_seed_localization real(r8) :: nocomp_patch_areas(0:numpft) ! vector of the total patch areas for each nocomp PFT ! If the dispersal kernel is not turned on, keep the dispersal fraction at zero @@ -2162,12 +2163,15 @@ subroutine SeedUpdate( currentSite ) ! special case: do we want to restrict each PFT's seeds to only go to patches with that nocomp PFT label? ! If so, then use a normalization factor that is one over the nocomp patch fraction for all patches of ! that PFT's nocomp label, and zero for all other patches. If we don't do this, then just set scalar to one. + ! Similarly, only add external seed rain to a given PFT's nocomp patches nocomp_seed_scaling = 1._r8 + seed_supply = EDPftvarcon_inst%seed_suppl(pft) if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then if (currentPatch%nocomp_pft_label .eq. pft) then nocomp_seed_scaling = AREA/nocomp_patch_areas(pft) else nocomp_seed_scaling = 0._r8 + seed_supply = 0._r8 endif endif @@ -2206,11 +2210,7 @@ subroutine SeedUpdate( currentSite ) ! Seed input from external sources (user param seed rain, or dispersal model) ! Include both prescribed seed_suppl and seed_in dispersed from neighbouring gridcells - seed_in_external = seed_stoich * currentSite%seed_in(pft)/area ![kg/m2/day] - !only add external seed rain to a given PFT's nocomp patches - if ( (hlm_use_nocomp .eq. ifalse) .or. (hlm_use_nocomp .eq. itrue .and. currentPatch%nocomp_pft_label .eq. pft) ) then - seed_in_external = seed_in_external + seed_stoich * EDPftvarcon_inst%seed_suppl(pft)*years_per_day ![kg/m2/day] - endif + seed_in_external = seed_stoich * (seed_supply*years_per_day + currentSite%seed_in(pft)/area ![kg/m2/day] litt%seed_in_extern(pft) = litt%seed_in_extern(pft) + seed_in_external ! Seeds entering externally [kg/site/day] From 39bafa347d1bb016209fba19b2e3aed6dd4526b7 Mon Sep 17 00:00:00 2001 From: jessica needham Date: Mon, 9 Jun 2025 17:10:34 -0700 Subject: [PATCH 082/194] fix to event based logging harvest --- biogeochem/EDLoggingMortalityMod.F90 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/biogeochem/EDLoggingMortalityMod.F90 b/biogeochem/EDLoggingMortalityMod.F90 index 82667f1fea..93c72019f4 100644 --- a/biogeochem/EDLoggingMortalityMod.F90 +++ b/biogeochem/EDLoggingMortalityMod.F90 @@ -15,6 +15,7 @@ module EDLoggingMortalityMod use FatesConstantsMod , only : r8 => fates_r8 use FatesConstantsMod , only : rsnbl_math_prec + use FatesConstantsMod , only : fates_unset_int use FatesCohortMod , only : fates_cohort_type use FatesPatchMod , only : fates_patch_type use EDTypesMod , only : site_massbal_type @@ -251,6 +252,10 @@ subroutine LoggingMortality_frac( currentSite, bc_in, pft_i, dbh, canopy_layer, ! todo: eventually set up distinct harvest practices, each with a set of input paramaeters ! todo: implement harvested carbon inputs + + ! Valid values are 0,1,2 so initialize to a different value + cur_harvest_tag = fates_unset_int + ! The transition_landuse_from_off_to_on is for handling the special case of the first timestep after leaving potential ! vegetation mode. In this case, all prior historical land-use, including harvest, needs to be applied on that first day. ! So logging rates on that day are what is required to deforest exactly the amount of primary lands that will give the @@ -288,6 +293,7 @@ subroutine LoggingMortality_frac( currentSite, bc_in, pft_i, dbh, canopy_layer, ! 0=use fates logging parameters directly when logging_time == .true. ! this means harvest the whole cohort area harvest_rate = 1._r8 + cur_harvest_tag = fates_bypass_harvest_debt else if (hlm_use_lu_harvest == itrue .and. hlm_harvest_units == hlm_harvest_area_fraction) then ! We are harvesting based on areal fraction, not carbon/biomass terms. From bd2c170896e0212a758919e1eca03c475926efaa Mon Sep 17 00:00:00 2001 From: Adrianna Foster Date: Wed, 11 Jun 2025 09:08:25 -0600 Subject: [PATCH 083/194] fix breaking changes --- biogeophys/CMakeLists.txt | 1 + testing/testing_shr/FatesFactoryMod.F90 | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/biogeophys/CMakeLists.txt b/biogeophys/CMakeLists.txt index b232b22a95..09221e0974 100644 --- a/biogeophys/CMakeLists.txt +++ b/biogeophys/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND fates_sources FatesHydroWTFMod.F90 + LeafBiophysicsMod.F90 FatesPlantHydraulicsMod.F90) sourcelist_to_parent(fates_sources) \ No newline at end of file diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index 1f4446887c..72ee6c3867 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -8,6 +8,7 @@ module FatesFactoryMod use FatesConstantsMod, only : isemi_stress_decid use FatesConstantsMod, only : primaryland use FatesConstantsMod, only : sec_per_day, days_per_year + use FatesCohortMod, only : default_regeneration use FatesGlobals, only : fates_log use FatesGlobals, only : endrun => fates_endrun use FatesCohortMod, only : fates_cohort_type @@ -56,7 +57,6 @@ module FatesFactoryMod use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : nleafage use FatesSizeAgeTypeIndicesMod, only : get_age_class_index - use EDParamsMod, only : regeneration_model use SyntheticPatchTypes, only : synthetic_patch_type use shr_log_mod, only : errMsg => shr_log_errMsg @@ -89,6 +89,8 @@ subroutine InitializeGlobals(step_size) element_pos(carbon12_element) = 1 call InitPRTGlobalAllometricCarbon() + hlm_regeneration_model = default_regeneration + allocate(ema_24hr) call ema_24hr%define(sec_per_day, step_size, moving_ema_window) allocate(fixed_24hr) @@ -445,7 +447,7 @@ subroutine PatchFactory(patch, age, area, num_swb, num_pft, num_levsoil, allocate(patch) call patch%Create(age, area, land_use_label_local, nocomp_pft_local, num_swb, & - num_pft, num_levsoil, tod_local, regeneration_model) + num_pft, num_levsoil, tod_local, default_regeneration) patch%patchno = 1 patch%younger => null() From 1eb49d805cb884947502d0ec3043266bd769032e Mon Sep 17 00:00:00 2001 From: Adrianna Foster Date: Wed, 11 Jun 2025 09:11:06 -0600 Subject: [PATCH 084/194] fix issues with cmakelists --- CMakeLists.txt | 19 +++++++++++++++++++ testing/cime_setup.md | 3 --- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ed5429a11..0883e7c5d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.4) list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY}) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../share/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../components/cmeps/cmake") FIND_PATH(NETCDFC_FOUND libnetcdf.a ${NETCDF_C_DIR}/lib) FIND_PATH(NETCDFF_FOUND libnetcdff.a ${NETCDF_FORTRAN_DIR}/lib) @@ -19,6 +20,23 @@ include(CIME_utils) set(HLM_ROOT "../../") +if (DEFINED ENV{ESMF_ROOT}) + list(APPEND CMAKE_MODULE_PATH $ENV{ESMF_ROOT}/cmake) +endif() +find_package(ESMF REQUIRED) + +# This adds include directories needed for ESMF +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_F90COMPILEPATHS} ") +# This (which is *not* done in the share CMakeLists.txt) adds all directories and +# libraries needed when linking ESMF, including any dependencies of ESMF. (But note that +# this does *not* include the "-lesmf" itself). In particular, note that this includes any +# link flags needed to link against PIO, which is needed on some systems (including +# derecho); bringing in these PIO-related link flags via this ESMF mechanism allows us to +# avoid explicitly including PIO as a link library, which wouldn't work on systems where +# there is no separate PIO library and instead ESMF is built with its internal PIO +# library. +link_libraries(${ESMF_INTERFACE_LINK_LIBRARIES}) + # Add source directories from other share code (csm_share, etc.) add_subdirectory(${HLM_ROOT}/share/src csm_share) add_subdirectory(${HLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) @@ -90,6 +108,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Directories and libraries to include in the link step link_directories(${CMAKE_CURRENT_BINARY_DIR}) +link_libraries(esmf) # Add the main test directory add_subdirectory(${HLM_ROOT}/src/fates/testing) diff --git a/testing/cime_setup.md b/testing/cime_setup.md index 8fd9148b59..93ec31aa42 100644 --- a/testing/cime_setup.md +++ b/testing/cime_setup.md @@ -72,9 +72,6 @@ Next set up some other environment variables: ```bash export ESMF_INSTALL_PREFIX=$ESMF_DIR/install_dir -export ESMF_NETCDF=split -export ESMF_NETCDF_INCLUDE=/usr/local/include -export ESMF_NETCDF_LIBPATH=/usr/local/lib export ESMF_COMM=openmpi export ESMF_COMPILER=gfortranclang ``` From 5374f3924350a96ac0492d6c1ff93f7737bb97c1 Mon Sep 17 00:00:00 2001 From: Adrianna Foster Date: Wed, 11 Jun 2025 09:20:54 -0600 Subject: [PATCH 085/194] final fixes --- testing/functional_testing/math_utils/FatesTestMathUtils.F90 | 5 +++-- testing/testing_shr/FatesFactoryMod.F90 | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/testing/functional_testing/math_utils/FatesTestMathUtils.F90 b/testing/functional_testing/math_utils/FatesTestMathUtils.F90 index 627eb2713b..1de134d1be 100644 --- a/testing/functional_testing/math_utils/FatesTestMathUtils.F90 +++ b/testing/functional_testing/math_utils/FatesTestMathUtils.F90 @@ -1,7 +1,7 @@ program FatesTestQuadSolvers use FatesConstantsMod, only : r8 => fates_r8 - use FatesUtilsMod, only : QuadraticRootsNSWC, QuadraticRootsSridharachary + use FatesUtilsMod, only : QuadraticRootsNSWC use FatesUtilsMod, only : GetNeighborDistance implicit none @@ -15,6 +15,7 @@ program FatesTestQuadSolvers real(r8) :: a(n), b(n), c(n) ! coefficients for quadratic solvers real(r8) :: root1(n) ! real part of first root of quadratic solver real(r8) :: root2(n) ! real part of second root of quadratic solver + logical :: err ! error interface @@ -42,7 +43,7 @@ end subroutine WriteQuadData c = (/1.0_r8, 12.0_r8, 3.0_r8, 1.1_r8/) do i = 1, n - call QuadraticRootsNSWC(a(i), b(i), c(i), root1(i), root2(i)) + call QuadraticRootsNSWC(a(i), b(i), c(i), root1(i), root2(i), err) end do call WriteQuadData(out_file, n, a, b, c, root1, root2) diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index 72ee6c3867..a1d031f717 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -8,7 +8,7 @@ module FatesFactoryMod use FatesConstantsMod, only : isemi_stress_decid use FatesConstantsMod, only : primaryland use FatesConstantsMod, only : sec_per_day, days_per_year - use FatesCohortMod, only : default_regeneration + use FatesConstantsMod, only : default_regeneration use FatesGlobals, only : fates_log use FatesGlobals, only : endrun => fates_endrun use FatesCohortMod, only : fates_cohort_type @@ -54,7 +54,7 @@ module FatesFactoryMod use FatesAllometryMod, only : bdead_allom use FatesAllometryMod, only : bstore_allom use FatesAllometryMod, only : carea_allom - use FatesInterfaceTypesMod, only : hlm_parteh_mode + use FatesInterfaceTypesMod, only : hlm_parteh_mode, hlm_regeneration_model use FatesInterfaceTypesMod, only : nleafage use FatesSizeAgeTypeIndicesMod, only : get_age_class_index use SyntheticPatchTypes, only : synthetic_patch_type From 9f5f73fbf7102c0de39634f12a18fbcf7ff9261d Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Thu, 12 Jun 2025 14:22:38 -0700 Subject: [PATCH 086/194] update parameter name and description --- fire/SFParamsMod.F90 | 30 ++++++++++++------------ parameter_files/fates_params_default.cdl | 26 ++++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/fire/SFParamsMod.F90 b/fire/SFParamsMod.F90 index d6c8530c25..85f9f53ea3 100644 --- a/fire/SFParamsMod.F90 +++ b/fire/SFParamsMod.F90 @@ -38,18 +38,18 @@ module SFParamsMod real(r8),protected, public :: SF_val_mid_moisture_Coeff(num_fuel_classes) real(r8),protected, public :: SF_val_mid_moisture_Slope(num_fuel_classes) ! Prescribed fire relevant parameters - real(r8),protected, public :: SF_val_rxfire_tpup ! temprature upper threshold for conducting RX fire - real(r8),protected, public :: SF_val_rxfire_tplw ! temprature lower threshold - real(r8),protected, public :: SF_val_rxfire_rhup ! relative humidity upper threshold - real(r8),protected, public :: SF_val_rxfire_rhlw ! relative humidity lower threshold - real(r8),protected, public :: SF_val_rxfire_wdup ! wind speed upper threshold - real(r8),protected, public :: SF_val_rxfire_wdlw ! wind speed lower threshold + real(r8),protected, public :: SF_val_rxfire_tpup ! temperature upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_tplw ! temperature lower threshold below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_rhup ! relative humidity upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_rhlw ! relative humidity lower threshold below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_wdup ! wind speed upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_wdlw ! wind speed lower threshold below which rx fire is disallowed real(r8),protected, public :: SF_val_rxfire_AB ! prescribed fire burned fraction per day - real(r8),protected, public :: SF_val_rxfire_minthreshold ! minimum fire energy of rx fire, for management outcomes really - real(r8),protected, public :: SF_val_rxfire_maxthreshold ! maximum fire energy - real(r8),protected, public :: SF_val_rxfire_fuel_min ! minimum fuel load at the patch for the need of rx fire management - real(r8),protected, public :: SF_val_rxfire_fuel_max ! maximum fuel load, above which might be risky for conducting rx fire - real(r8),protected, public :: SF_val_rxfire_min_frac ! minimum fraction needs to be burnable at site level for conducting rx fire + real(r8),protected, public :: SF_val_rxfire_min_threshold ! minimum fire energy at or below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_max_threshold ! maximum fire energy at or above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_fuel_min ! minimum fuel load at or below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_fuel_max ! maximum fuel load at or above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_min_frac ! minimum burnable fraction at site level at or above which rx fire is allowed character(len=param_string_length),parameter :: SF_name_fdi_alpha = "fates_fire_fdi_alpha" character(len=param_string_length),parameter :: SF_name_miner_total = "fates_fire_miner_total" @@ -191,8 +191,8 @@ subroutine SpitFireParamsInit() SF_val_rxfire_wdup = nan SF_val_rxfire_wdlw = nan SF_val_rxfire_AB = nan - SF_val_rxfire_minthreshold = nan - SF_val_rxfire_maxthreshold = nan + SF_val_rxfire_min_threshold = nan + SF_val_rxfire_max_threshold = nan SF_val_rxfire_fuel_min = nan SF_val_rxfire_fuel_max = nan SF_val_rxfire_min_frac = nan @@ -368,10 +368,10 @@ subroutine SpitFireReceiveScalars(fates_params) data=SF_val_rxfire_AB) call fates_params%RetrieveParameter(name=SF_name_rxfire_min_threshold, & - data=SF_val_rxfire_minthreshold) + data=SF_val_rxfire_min_threshold) call fates_params%RetrieveParameter(name=SF_name_rxfire_max_threshold, & - data=SF_val_rxfire_maxthreshold) + data=SF_val_rxfire_max_threshold) call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_min, & data=SF_val_rxfire_fuel_min) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index b19bb7fd1e..ab4349710e 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -899,40 +899,40 @@ variables: fates_rxfire_switch:long_name = "management fire mode, 1 = use management fire, 0 = turn off management fire" ; double fates_rxfire_temp_upthreshold ; fates_rxfire_temp_upthreshold:units = "degree C"; - fates_rxfire_temp_upthreshold:long_name= "maximum temprature threshold for conducting prescribed fire"; + fates_rxfire_temp_upthreshold:long_name= "maximum temprature threshold above which prescribed fire is disallowed"; double fates_rxfire_temp_lwthreshold ; fates_rxfire_temp_lwthreshold:units = "degree C"; - fates_rxfire_temp_lwthreshold:long_name= "minimum temprature threshold for conducting prescribed fire"; + fates_rxfire_temp_lwthreshold:long_name= "minimum temprature threshold below which prescribe fire is disallowed"; double fates_rxfire_rh_upthreshold ; fates_rxfire_rh_upthreshold:units = "%"; - fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold for conducting prescribed fire"; + fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold above which prescribed fire is disallowed"; double fates_rxfire_rh_lwthreshold ; fates_rxfire_rh_lwthreshold:units = "%"; - fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold for conducting prescribed fire"; + fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold below which prescribed fire is disallowed"; double fates_rxfire_wind_upthreshold ; fates_rxfire_wind_upthreshold:units = "m/s"; - fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold for conducting prescribed fire"; + fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold above which prescribed fire is disallowed"; double fates_rxfire_wind_lwthreshold ; fates_rxfire_wind_lwthreshold:units = "m/s"; - fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold for conducting prescribed fire"; + fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold below which prescribed fire is disallowed"; double fates_rxfire_AB ; fates_rxfire_AB:units = "fraction/day"; fates_rxfire_AB:long_name= "daily burn capacity of prescribed fire"; double fates_rxfire_min_threshold ; - fates_rxfire_min_threshold:units = "kJ/m/s or kW/s"; - fates_rxfire_min_threshold:long_name= "minimum energy threshold for conducting prescribed fire"; + fates_rxfire_min_threshold:units = "kJ/m/s or kW/m"; + fates_rxfire_min_threshold:long_name= "minimum energy threshold at or above which prescribed fire is disallowed"; double fates_rxfire_max_threshold ; - fates_rxfire_max_threshold:units = "kJ/m/s or kW/s"; - fates_rxfire_max_threshold:long_name= "maximum energy threshold for conducting prescribed fire"; + fates_rxfire_max_threshold:units = "kJ/m/s or kW/m"; + fates_rxfire_max_threshold:long_name= "maximum energy threshold at or above which prescribed fire is disallowed"; double fates_rxfire_fuel_min ; fates_rxfire_fuel_min:units = "kgC/m2"; - fates_rxfire_fuel_min:long_name= "minimum fuel load at the patch level for prescribed fire to occur"; + fates_rxfire_fuel_min:long_name= "minimum fuel load at or below which prescribed fire is disallowed"; double fates_rxfire_fuel_max ; fates_rxfire_fuel_max:units = "kgC/m2"; - fates_rxfire_fuel_max:long_name= "maximum fuel load above which prescribed fire can be risky"; + fates_rxfire_fuel_max:long_name= "maximum fuel load at or above which prescribed fire is disallowed"; double fates_rxfire_min_frac ; fates_rxfire_min_frac:units = "fraction"; - fates_rxfire_min_frac:long_name="minimum fraction of land needs to be burnable for conducting rx fire"; + fates_rxfire_min_frac:long_name="minimum fraction of land needs to be burnable to allow rx fire"; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; From 37a61ab745505db5f1933958065488dd69a05272 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 12 Jun 2025 15:50:50 -0600 Subject: [PATCH 087/194] run_unit_testing.py: Enable running from anywhere. --- testing/run_unit_tests.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testing/run_unit_tests.py b/testing/run_unit_tests.py index f9bd344b39..6ba6d4cbca 100755 --- a/testing/run_unit_tests.py +++ b/testing/run_unit_tests.py @@ -29,8 +29,9 @@ from CIME.utils import run_cmd_no_fail # pylint: disable=wrong-import-position,import-error,wrong-import-order # constants for this script -_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../") -_DEFAULT_CONFIG_FILE = "unit_tests.cfg" +_FILE_DIR = os.path.dirname(os.path.abspath(__file__)) +_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir) +_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "unit_tests.cfg") _TEST_SUB_DIR = "testing" From 7c61be7a2d059865e92a44e2c8fbfe5bb264dabf Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 12 Jun 2025 16:02:58 -0600 Subject: [PATCH 088/194] run_unit_tests.py: Fail if config_file doesn't exist or is a dir. --- testing/utils.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/utils.py b/testing/utils.py index 06fd74db0b..cd1973b5b7 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -132,6 +132,13 @@ def config_to_dict(config_file: str) -> dict: Returns: dictionary: dictionary of config file """ + + # Check that config file exists and is a file + if not os.path.exists(config_file): + raise FileNotFoundError(config_file) + if not os.path.isfile(config_file): + raise RuntimeError(f"config_file is a directory: '{config_file}'") + config = configparser.ConfigParser() config.read(config_file) From f35b05f03a1f9ed976bf75af0543fc324647927f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Thu, 12 Jun 2025 16:03:36 -0600 Subject: [PATCH 089/194] run_unit_tests.py: Add optional --config-file argument. --- testing/run_unit_tests.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/testing/run_unit_tests.py b/testing/run_unit_tests.py index 6ba6d4cbca..a0f84532d0 100755 --- a/testing/run_unit_tests.py +++ b/testing/run_unit_tests.py @@ -59,6 +59,13 @@ def commandline_args(): "Will be created if it does not exist.\n", ) + parser.add_argument( + "--config-file", + type=str, + default=_DEFAULT_CONFIG_FILE, + help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'", + ) + parser.add_argument( "--make-j", type=int, @@ -130,9 +137,8 @@ def main(): Reads in command-line arguments and then runs the tests. """ - full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) - args = commandline_args() + full_test_dict = config_to_dict(args.config_file) test_dict = parse_test_list(full_test_dict, args.test_list) run_unit_tests( From 6c6e96763eed7f78c67b836c43701796ca5d2f2e Mon Sep 17 00:00:00 2001 From: adrifoster Date: Fri, 13 Jun 2025 09:45:42 -0600 Subject: [PATCH 090/194] fix hlm_regen use --- testing/testing_shr/FatesFactoryMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index a1d031f717..c9cbd100f3 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -447,7 +447,7 @@ subroutine PatchFactory(patch, age, area, num_swb, num_pft, num_levsoil, allocate(patch) call patch%Create(age, area, land_use_label_local, nocomp_pft_local, num_swb, & - num_pft, num_levsoil, tod_local, default_regeneration) + num_pft, num_levsoil, tod_local, hlm_regeneration_model) patch%patchno = 1 patch%younger => null() From 38fefa71c9d78cb038c6cc2627299175a8da577a Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 13 Jun 2025 09:28:26 -0700 Subject: [PATCH 091/194] refactor canopy promotion/demotion for simplicity, stability and code reusability. --- biogeochem/EDCanopyStructureMod.F90 | 1316 ++++++++------------------- biogeochem/EDCohortDynamicsMod.F90 | 32 +- biogeochem/FatesPatchMod.F90 | 29 +- main/EDParamsMod.F90 | 8 +- 4 files changed, 416 insertions(+), 969 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 081c734dd5..707dfb7f13 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -72,8 +72,21 @@ module EDCanopyStructureMod integer :: istat ! return status code character(len=255) :: smsg ! Message string for deallocation errors + + ! Precision targets for demotion and promotion + ! We have two: + ! "pa_area_target_precision" is the required precision at the patch level, + ! we keep shuffling and splitting cohorts until each layer is within this precision + ! "co_area_target_precision" is the required precision at the cohort level, + ! essentially it is the minimum amount of change required to not ignore + ! a partial promotion or demotion + + real(r8), parameter :: pa_area_target_precision = 1.0E-11_r8 + real(r8), parameter :: co_area_target_precision = 1.0E-12_r8 + + integer, parameter :: demotion_phase = 1 + integer, parameter :: promotion_phase = 2 - real(r8), parameter :: area_target_precision = 1.0E-11_r8 ! Area conservation ! will attempt to reduce errors ! below this level @@ -86,7 +99,13 @@ module EDCanopyStructureMod ! can be roughly considered the same right? logical, parameter :: preserve_b4b = .true. - + + + ! If we want to allow some degree of imperfection in canopy closer, we would + ! add it here + real(r8), parameter :: imperfect_fraction = 0._r8 + + ! 10/30/09: Created by Rosie Fisher ! 2017/2018: Modifications and updates by Ryan Knox ! ============================================================================ @@ -134,7 +153,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! ! !USES: - use EDParamsMod, only : ED_val_comp_excln + use EDParamsMod, only : comp_excln_exp use EDTypesMod , only : min_patch_area ! @@ -149,7 +168,7 @@ subroutine canopy_structure( currentSite , bc_in ) integer :: i_lyr ! current layer index integer :: z ! Current number of canopy layers. (1= canopy, 2 = understorey) integer :: ipft - real(r8) :: arealayer(nclmax+2) ! Amount of plant area currently in each canopy layer + real(r8) :: arealayer(nclmax+5) ! Amount of plant area currently in each canopy layer integer :: patch_area_counter ! count iterations used to solve canopy areas logical :: area_not_balanced ! logical controlling if the patch layer areas ! have successfully been redistributed @@ -184,7 +203,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! canopy layer has a special bounds check currentCohort => currentPatch%tallest do while (associated(currentCohort)) - if( currentCohort%canopy_layer < 1 .or. currentCohort%canopy_layer > nclmax+1 ) then + if( currentCohort%canopy_layer < 1 ) then write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon write(fates_log(),*) 'BOGUS CANOPY LAYER: ',currentCohort%canopy_layer @@ -212,14 +231,16 @@ subroutine canopy_structure( currentSite , bc_in ) ! Calculate how many layers we have in this canopy ! This also checks the understory to see if its crown ! area is large enough to warrant a temporary sub-understory layer - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + z = NumCanopyLayers(currentPatch) do i_lyr = 1,z ! Loop around the currently occupied canopy layers. - call DemoteFromLayer(currentSite, currentPatch, i_lyr, bc_in) + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) + target_area = max(0._r8,arealayer(i_lyr) - (1._r8-imperfect_fraction)*currentPatch%area) + call PromoteOrDemote(currentSite, currentPatch, i_lyr, demotion_phase, target_area) end do - ! After demotions, we may then again have cohorts that are very very - ! very sparse, remove them + ! After demotions, we may then again have cohorts that + ! are very very very sparse, remove them call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) call fuse_cohorts(currentSite, currentPatch, bc_in) @@ -227,20 +248,20 @@ subroutine canopy_structure( currentSite , bc_in ) ! Remove cohorts for various other reasons call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) - ! --------------------------------------------------------------------------------------- ! Promotion Phase: Identify if any upper-layers are underful and layers below them ! have cohorts that can be split and promoted to the layer above. ! --------------------------------------------------------------------------------------- - ! Re-calculate Number of layers without the false substory - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + ! Re-calculate Number of layers + z = NumCanopyLayers(currentPatch) ! We only promote if we have at least two layers if (z>1) then - - do i_lyr=1,z-1 - call PromoteIntoLayer(currentSite, currentPatch, i_lyr) + do i_lyr=2,z + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr-1,arealayer(i_lyr-1)) + target_area = max(0._r8,(1._r8-imperfect_fraction)*currentPatch%area - arealayer(i_lyr-1)) + call PromoteOrDemote(currentSite, currentPatch, i_lyr, promotion_phase, target_area) end do ! Remove cohorts that are incredibly sparse @@ -257,18 +278,26 @@ subroutine canopy_structure( currentSite , bc_in ) ! Check on Layer Area (if the layer differences are not small ! Continue trying to demote/promote. Its possible on the first pass through, ! that cohort fusion has nudged the areas a little bit. + ! On all but the bottom layer, we expect the areas to match the area of the + ! patch with small precision, since we assume a PPA. On the lowest layer, + ! we only expect the area to be below the patch area. ! --------------------------------------------------------------------------------------- - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + z = NumCanopyLayers(currentPatch) area_not_balanced = .false. do i_lyr = 1,z call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) - if( ((arealayer(i_lyr)-currentPatch%area)/currentPatch%area > area_check_rel_precision) .or. & - ((arealayer(i_lyr)-currentPatch%area) > area_check_precision ) ) then - area_not_balanced = .true. - endif + if(i_lyr < z)then + if (abs(arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + area_not_balanced = .true. + end if + else + if ((arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + area_not_balanced = .true. + end if + end if enddo - + ! --------------------------------------------------------------------------------------- ! Gracefully exit if too many iterations have gone by ! --------------------------------------------------------------------------------------- @@ -277,10 +306,14 @@ subroutine canopy_structure( currentSite , bc_in ) if(patch_area_counter > max_patch_iterations .and. area_not_balanced) then write(fates_log(),*) 'PATCH AREA CHECK NOT CLOSING' write(fates_log(),*) 'patch area:',currentpatch%area + write(fates_lot(),*) 'fraction that is imperfect (unclosed):',imperfect_fraction do i_lyr = 1,z write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) - write(fates_log(),*) 'rel error: ',(arealayer(i_lyr)-currentPatch%area)/currentPatch%area - write(fates_log(),*) 'abs error: ',arealayer(i_lyr)-currentPatch%area + write(fates_log(),*) 'rel error: ',(arealayer(i_lyr)- & + (1._r8-imperfect_fraction)*currentPatch%area)/ & + ((1._r8-imperfect_fraction)*currentPatch%area) + write(fates_log(),*) 'abs error: ',arealayer(i_lyr) - & + (1._r8-imperfect_fraction)*currentPatch%area enddo write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon @@ -307,10 +340,16 @@ subroutine canopy_structure( currentSite , bc_in ) enddo ! do while(area_not_balanced) - ! Save number of canopy layers to the patch structure + ! Terminate any cohorts that are still outside the maximum number of + ! canopy layers. These terminations only occur in level 3 + call terminate_cohorts(currentSite, currentPatch, 3,17,bc_in) + + z = NumCanopyLayers(currentPatch) + ! Save number of canopy layers to the patch structure if(z > nclmax) then - write(fates_log(),*) 'Termination should have ensured number of canopy layers was not larger than nclmax' + write(fates_log(),*) 'Termination should have ensured number' + write(fates_log(),*) 'of canopy layers was not larger than nclmax' write(fates_log(),*) 'Predicted: ',z write(fates_log(),*) 'nclmax: ',nclmax write(fates_log(),*) 'Consider increasing nclmax if this value is to low' @@ -327,7 +366,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! neighbor is in level 2 set zstar as the ehight of that shortest level 1 cohort ! ------------------------------------------------------------------------------------------- - if ( ED_val_comp_excln .lt. 0.0_r8) then + if ( comp_excln_exp .lt. 0.0_r8) then currentPatch%zstar = 0._r8 currentCohort => currentPatch%tallest do while (associated(currentCohort)) @@ -348,905 +387,315 @@ subroutine canopy_structure( currentSite , bc_in ) return end subroutine canopy_structure - ! ============================================================================================== + subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) - subroutine DemoteFromLayer(currentSite,currentPatch,i_lyr,bc_in) - - use EDParamsMod, only : ED_val_comp_excln + ! -------------------------------------------------------------- + ! This routine will: + ! 1) Identify the list of cohorts that are in the appropriate + ! layer for promotion or demotion into the adjacent + ! layer + ! 2) Calculate the combined crown area of those cohorts + ! that will be transferre to the adjacent layer + ! 3) Perform the transfer either by re-assignment (if whole) + ! of by splitting the cohort + ! 4) Track the abundance and mass flows when promoting/demoting + ! -------------------------------------------------------------- - ! !ARGUMENTS - type(ed_site_type), intent(inout) :: currentSite - type(fates_patch_type), intent(inout) :: currentPatch - integer, intent(in) :: i_lyr ! Current canopy layer of interest - type(bc_in_type), intent(in) :: bc_in + ! Arguments + type(ed_site_type) :: site + type(fates_patch_type) :: patch + integer,intent(in) :: target_layer ! Canopy layer we draw from + integer,intent(in) :: phase ! promotion or demotion? + real(r8),intent(in) :: target_area ! Area we want to move [m2/ha] - ! !LOCAL VARIABLES: - type(fates_cohort_type), pointer :: currentCohort + ! Locals + type(fates_cohort_type), pointer :: cohort type(fates_cohort_type), pointer :: copyc - type(fates_cohort_type), pointer :: nextc ! The next cohort in line - integer :: i_cwd ! Index for CWD pool - real(r8) :: cc_loss ! cohort crown area loss in demotion (m2) - real(r8) :: leaf_c ! leaf carbon [kg] - real(r8) :: fnrt_c ! fineroot carbon [kg] - real(r8) :: sapw_c ! sapwood carbon [kg] - real(r8) :: store_c ! storage carbon [kg] - real(r8) :: struct_c ! structure carbon [kg] - real(r8) :: scale_factor ! for prob. exclusion - scales weight to a fraction - real(r8) :: scale_factor_min ! "" minimum before exeedance of 1 - real(r8) :: scale_factor_res ! "" applied to residual areas - real(r8) :: area_res ! residual area to demote after weakest cohort hits max - real(r8) :: newarea - real(r8) :: demote_area - real(r8) :: sumweights - real(r8) :: sumequal ! for rank-ordered same-size cohorts - ! this tallies their excluded area - real(r8) :: arealayer ! the area of the current canopy layer - logical :: tied_size_with_neighbors - real(r8) :: total_crownarea_of_tied_cohorts - - ! First, determine how much total canopy area we have in this layer - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) - - demote_area = arealayer - currentPatch%area - - if ( demote_area > area_target_precision ) then - - ! Is this layer currently over-occupied? - ! In that case, we need to work out which cohorts to demote. - ! We go in order from shortest to tallest for ranked demotion - - sumweights = 0.0_r8 - currentCohort => currentPatch%shortest - do while (associated(currentCohort)) - call carea_allom(currentCohort%dbh,currentCohort%n, & - currentSite%spread,currentCohort%pft, & - currentCohort%crowndamage, currentCohort%c_area) - - if(debug) then - if(currentCohort%c_area<0._r8)then - write(fates_log(),*) 'negative c_area stage 1d: ',currentCohort%dbh,i_lyr,currentCohort%n, & - currentSite%spread,currentCohort%pft,currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - if( currentCohort%canopy_layer == i_lyr)then - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - ! ---------------------------------------------------------- - ! Stochastic method. - ! Weight cohort demotion by inverse size to a constant power. - ! In this hypothesis, it is assumed that even the tallest - ! cohorts have a chance (although smaller) of being forced - ! to the understory. - ! ---------------------------------------------------------- - - currentCohort%excl_weight = 1._r8 / (currentCohort%height**ED_val_comp_excln) - sumweights = sumweights + currentCohort%excl_weight - - else - - ! ----------------------------------------------------------- - ! Rank ordered deterministic method - ! ----------------------------------------------------------- - ! If there are cohorts that have the exact same height (which is possible, really) - ! we don't want to unilaterally promote/demote one before the others. - ! So we <>mote them as a unit - ! now we need to go through and figure out how many equal-size cohorts there are. - ! then we need to go through, add up the collective crown areas of all equal-sized - ! and equal-canopy-layer cohorts, - ! and then demote from each as if they were a single group - - total_crownarea_of_tied_cohorts = currentCohort%c_area - - tied_size_with_neighbors = .false. - nextc => currentCohort%taller - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if( nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - tied_size_with_neighbors = .true. - total_crownarea_of_tied_cohorts = & - total_crownarea_of_tied_cohorts + nextc%c_area - end if - else - exit - endif - nextc => nextc%taller - end do - - if ( tied_size_with_neighbors ) then - - currentCohort%excl_weight = & - max(0.0_r8,min(currentCohort%c_area, & - (currentCohort%c_area/total_crownarea_of_tied_cohorts) * & - (demote_area - sumweights) )) - - sumequal = currentCohort%excl_weight - - nextc => currentCohort%taller - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if (nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - ! now we know the total crown area of all equal-sized, - ! equal-canopy-layer cohorts - nextc%excl_weight = & - max(0.0_r8,min(nextc%c_area, & - (nextc%c_area/total_crownarea_of_tied_cohorts) * & - (demote_area - sumweights) )) - sumequal = sumequal + nextc%excl_weight - end if - else - exit - endif - nextc => nextc%taller - end do - - ! Update the current cohort pointer to the last similar cohort - ! Its ok if this is not in the right layer - if(associated(nextc))then - currentCohort => nextc%shorter - else - currentCohort => currentPatch%tallest - end if - sumweights = sumweights + sumequal - - else - currentCohort%excl_weight = & - max(min(currentCohort%c_area, demote_area - sumweights ), 0._r8) - sumweights = sumweights + currentCohort%excl_weight - end if - - endif - endif - currentCohort => currentCohort%taller - enddo - - ! If this is probabalistic demotion, we need to do a round of normalization. - ! And then a few rounds where we pre-calculate the demotion areas - ! and adjust things if the demoted area wants to be greater than - ! what is available. The math is too hard to explain here, see - ! the tech note section on promotion/demotion. - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - scale_factor_min = 1.e10_r8 - scale_factor = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - if(currentCohort%canopy_layer == i_lyr) then - - currentCohort%excl_weight = currentCohort%excl_weight/sumweights - if( 1._r8/currentCohort%excl_weight < scale_factor_min ) & - scale_factor_min = 1._r8/currentCohort%excl_weight - - scale_factor = scale_factor + currentCohort%excl_weight * currentCohort%c_area - - endif - currentCohort => currentCohort%shorter - enddo - - ! This is the factor by which we need to multiply - ! the demotion probabilities, so the sum result equals - ! the total amount to demote - - scale_factor = demote_area/scale_factor - - if(scale_factor <= scale_factor_min) then - - ! Trivial case, all of the demotion fractions are less than 1. - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - currentCohort%excl_weight = currentCohort%c_area * currentCohort%excl_weight * scale_factor - - if(debug) then - if((currentCohort%excl_weight > (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%excl_weight < 0._r8) ) then - write(fates_log(),*) 'exclusion area too big (1)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'dbh: ',currentCohort%dbh - write(fates_log(),*) 'n: ',currentCohort%n - write(fates_log(),*) 'spread: ',currentSite%spread - write(fates_log(),*) 'pft: ',currentCohort%pft - write(fates_log(),*) 'currentCohort%excl_weight: ',currentCohort%excl_weight - write(fates_log(),*) 'excess: ',currentCohort%excl_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - else - - - ! Non-trivial case, at least 1 cohort's demotion - ! rate would exceed its area, given the trivial scale factor - - area_res = 0._r8 - scale_factor_res = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - area_res = area_res + & - currentCohort%c_area * currentCohort%excl_weight * & - scale_factor_min - scale_factor_res = scale_factor_res + & - currentCohort%c_area * & - (1._r8 - (currentCohort%excl_weight * scale_factor_min)) - endif - currentCohort => currentCohort%shorter - enddo - - area_res = demote_area - area_res - - scale_factor_res = area_res / scale_factor_res - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - - currentCohort%excl_weight = currentCohort%c_area * & - (currentCohort%excl_weight * scale_factor_min + & - (1._r8 - (currentCohort%excl_weight*scale_factor_min) ) * scale_factor_res) - - if(debug)then - if((currentCohort%excl_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%excl_weight < 0._r8) ) then - write(fates_log(),*) 'exclusion area error (2)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%excl_weight: ', & - currentCohort%excl_weight - write(fates_log(),*) 'excess: ', & - currentCohort%excl_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - end if - - end if - - - ! perform a check and see if the demotions meet the demand - sumweights = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - sumweights = sumweights + currentCohort%excl_weight - end if - currentCohort => currentCohort%shorter - end do - - if (abs(sumweights - demote_area) > area_check_precision ) then - write(fates_log(),*) 'demotions dont add up' - write(fates_log(),*) 'sum demotions: ',sumweights - write(fates_log(),*) 'area needed to be demoted: ',demote_area - write(fates_log(),*) 'excess: ',sumweights - demote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - - ! Weights have been calculated. Now move them to the lower layer - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - nextc => currentCohort%shorter - - if(currentCohort%canopy_layer == i_lyr )then - - cc_loss = currentCohort%excl_weight - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - if ( (cc_loss-currentCohort%c_area) > -nearzero .and. & - (cc_loss-currentCohort%c_area) < area_target_precision ) then - - ! If the whole cohort is being demoted, just change its - ! layer index - - currentCohort%canopy_layer = i_lyr+1 - - ! keep track of number and biomass of demoted cohort - currentSite%demotion_rate(currentCohort%size_class) = & - currentSite%demotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%demotion_carbonflux = currentSite%demotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * currentCohort%n - - elseif( (cc_loss < currentCohort%c_area) .and. & - (cc_loss > area_target_precision) ) then - - ! If only part of the cohort is demoted - ! then it must be split (little more complicated) - - ! Make a copy of the current cohort. The copy and the original - ! conserve total number density of the original. The copy - ! remains in the upper-story. The original is the one - ! demoted to the understory - - - allocate(copyc) - - ! (keep as an example) - ! Initialize running means - !allocate(copyc%tveg_lpa) - !!allocate(copyc%l2fr_ema) - ! Note, no need to give a starter value here, - ! that will be taken care of in copy() - !!call copyc%l2fr_ema%InitRMean(ema_60day) - - ! Initialize the PARTEH object and point to the - ! correct boundary condition fields - copyc%prt => null() - call InitPRTObject(copyc%prt) - - if( hlm_use_planthydro.eq.itrue ) then - call InitHydrCohort(currentSite,copyc) - endif - - call currentCohort%Copy(copyc) - call copyc%InitPRTBoundaryConditions() - - newarea = currentCohort%c_area - cc_loss - copyc%n = currentCohort%n*newarea/currentCohort%c_area - currentCohort%n = currentCohort%n - copyc%n - - copyc%canopy_layer = i_lyr !the taller cohort is the copy - - ! Demote the current cohort to the understory. - currentCohort%canopy_layer = i_lyr + 1 - - ! keep track of number and biomass of demoted cohort - currentSite%demotion_rate(currentCohort%size_class) = & - currentSite%demotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%demotion_carbonflux = currentSite%demotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * currentCohort%n - - call carea_allom(copyc%dbh,copyc%n,currentSite%spread,copyc%pft, & - copyc%crowndamage, copyc%c_area) - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - - !----------- Insert copy into linked list ------------------------! - copyc%shorter => currentCohort - if(associated(currentCohort%taller))then - copyc%taller => currentCohort%taller - currentCohort%taller%shorter => copyc - else - currentPatch%tallest => copyc - copyc%taller => null() - endif - currentCohort%taller => copyc - - elseif(cc_loss > currentCohort%c_area)then - - write(fates_log(),*) 'more area than the cohort has is being demoted' - write(fates_log(),*) 'loss:',cc_loss - write(fates_log(),*) 'existing area:',currentCohort%c_area - write(fates_log(),*) 'excess: ',cc_loss - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - - end if - - ! kill the ones which go into canopy layers that are not allowed - ! USE THIS OVERRIDE IF YOU ARE FORCING A ONE COHORT SIMULATION - ! (also make sure to turn off germination, external seed rain, - ! (use only one PFT, and make sure disturb_frac is 0) - ! (RGK-0822) - !if(currentCohort%canopy_layer>1) then - - if(currentCohort%canopy_layer>nclmax )then - ! put the litter from the terminated cohorts - ! straight into the fragmenting pools - call terminate_cohort(currentSite,currentPatch,currentCohort,bc_in,i_term_mort_type_canlev) - deallocate(currentCohort, stat=istat, errmsg=smsg) - if (istat/=0) then - write(fates_log(),*) 'dealloc012: fail on deallocate(currentCohort):'//trim(smsg) - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - else - call carea_allom(currentCohort%dbh,currentCohort%n, & - currentSite%spread,currentCohort%pft,currentCohort%crowndamage, & - currentCohort%c_area) - end if - - endif !canopy layer = i_ly - - ! We dont use our typical (point to smaller) - ! here, because, we may had deallocated the existing - ! currentCohort - - currentCohort => nextc - enddo !currentCohort - - ! Update the area calculations of the current layer - ! And the layer below that may or may not had recieved - ! Demotions - - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) - - if ( (abs(arealayer - currentPatch%area)/arealayer > area_check_rel_precision ) .or. & - (abs(arealayer - currentPatch%area) > area_check_precision) ) then - write(fates_log(),*) 'demotion did not trim area within tolerance' - write(fates_log(),*) 'arealayer:',arealayer - write(fates_log(),*) 'patch%area:',currentPatch%area - write(fates_log(),*) 'ilayer: ',i_lyr - write(fates_log(),*) 'bias:',arealayer - currentPatch%area - write(fates_log(),*) 'rel bias:',(arealayer - currentPatch%area)/arealayer - write(fates_log(),*) 'demote_area:',demote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + real(r8) :: sumpd_carea ! Sum crown area of all cohorts in layer [m2/ha] + real(r8) :: group_area ! Sum area of cohorts with the same height [m2/ha] + real(r8) :: remainder_area ! The area that has not been accounted + real(r8) :: excess_area ! The area that could not be accounted + real(r8) :: attempt_area ! Amount of area attempted to donate probabilistically + real(r8) :: max_donate_area ! This is the total area of the layer + ! for as seeks to fill out the target_area + real(r8) :: leaf_c, store_c + real(r8) :: fnrt_c, sapw_c + real(r8) :: struct_c + integer :: ilyr_change ! layer offset from current for the destination (+/- 1) + integer :: ic,ic_n,ic_nn ! Cohort indices + integer :: n_layer ! The number of cohorts in the layer + + + if (target_area patch%co_scr) + + + + ! Step 1: Determine which cohorts are in the layer + ! and point to them in the scratch vector + ! Make sure their areas are updated too. + ! We point to them in the scratch + ! vector in order of promotion/demotion, + ! note that this is inconsequential for probabalistic + + ic = 0 + layer_area = 0._r8 + if(phase==demotion_phase) then + cohort => patch%shortest + ilyr_change = -1 + else + cohort => patch%tallest + ilyr_change = 1 + end if + do while (associated(cohort)) + if(cohort%canopy_layer == target_layer)then + ic = ic + 1 + call carea_allom(cohort%dbh,cohort%n,site%spread, & + cohort%pft,cohort%crowndamage,cohort%c_area) + layer_area = layer_area + cohort%c_area + layer_co(ic)%p => cohort + end if + if(phase==demotion_phase) then + cohort => cohort%taller + else + cohort => cohort%shorter + end if + end do + + ! We update the target area to be no more than the + ! area of the layer (can't take more than there is..) + target_area = min(target_area,layer_area) + + + ! Store the number of cohorts in the layer + ! and zero out the array of area transfers + n_layer = ic + do ic = 1,n_layer + layer_co(ic)%pd_area = 0._r8 + end do + + ! Step 2: Calculate the promotion or demotion areas + comp_excl_type: if (comp_excln_exp .ge. 0.0_r8 ) then + + ! ------------------------------------------------------------------ + ! Stochastic case + ! ------------------------------------------------------------------ + + sumpd_area = 0._r8 + do ic = 1,n_layer + cohort => layer_co(ic)%p + if(phase==demotion_phase) then + layer_co(ic)%pd_area = cohort%c_area/(cohort%height**comp_excln_exp) + elseif(phase==promotion_phase) then + layer_co(ic)%pd_area = cohort%c_area*cohort%height**comp_excln_exp + end if + sumpd_area = sumpd_area + layer_co(ic)%pd_area + end do + + ! Distribute areas in a first pass + ! For those cohorts where more area was to be donated + ! than it has, accumulate the excess. For those + ! cohorts that are not filled and still have area to + ! donate, accumulate remainder. We will use these in + ! the next step to portion out area. + + excess_area = 0._r8 + remainder_area = 0._r8 + do ic = 1,n_layer + cohort => layer_co(ic)%p + attempt_area = target_area*layer_co(ic)%pd_area/sumpd_area + if(attempt_area>cohort%c_area)then + excess_area = excess_area + (attempt_area - cohort%c_area) + else + remainder_area = remainder_area + (cohort%c_area - attempt_area) + end if + layer_co(ic)%pd_area = min(cohort%c_area,attempt_area) + end do + + if(excess_area>nearzero)then + do ic = 1,n_layer + cohort => layer_co(ic)%p + ! look at just the cohorts that still have space to give + ! remove from them the same fraction of their remaining space + if (abs(layer_co(ic)%pd_area-cohort%c_area) > nearzero) then + layer_co(ic)%pd_area = layer_co(ic)%pd_area + & + (excess_area/remainder) * & + (cohort%c_area - layer_co(ic)%pd_area) + end if + end do + end if + + + else !comp_excl_exp < 0 + + ! ------------------------------------------------------------------ + ! Rank Ordered Case + ! ------------------------------------------------------------------ + + sumpd_area = 0._r8 + ic = 1 + do while( ic<=n_layer .and. (target_area-sumpd_area)>co_area_target_precision) + + cohort => layer_co(ic)%p + + ! Determine if the next cohorts in + ! order have the same height + + group_area = cohort%c_area + ic_n = ic + check_next:do while(ic_n similar_height_tol ) then + exit check_next + else + ic_n = ic_n + 1 + group_area = group_area+layer_co(ic_n)%p%c_area + end if + end do check_next + + remainder_area = min(target_area-sumpd_area,group_area) + do ic_nn = ic,ic_n + layer_co(ic_nn)%pd_area = remainder_area*layer_co(ic_nn)%p%c_area/norm_area + sumpd_area = sumpd_area + layer_co(ic_nn)%pd_area + end do + + ic = ic_n + 1 + + end do + end if comp_excl_type + + ! Check to make sure the changes are within bounds + do ic = 1,n_layer + cohort => layer_co(ic)%p + if( (layer_co(ic)%pd_area > cohort%c_area) .or. & + (layer_co(ic)%pd_area < 0._r8) ) then + write(fates_log(),*) 'negative,or more area than the cohort has is being promoted/demoted' + write(fates_log(),*) 'change: ',layer_co(ic)%pd_area + write(fates_log(),*) 'existing area:',cohort%c_area + write(fates_log(),*) 'excess: ',layer_co(ic)%pd_area - cohort%c_area + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end do + + ! Part 3: + ! Apply the area changes by splitting the cohort and re-assigning + ! either all or part of it to a new layer + + ic_loop0: do ic = 1,n_layer + + cohort => layer_co(ic)%p + + ! If the demotion area is the same area as the + ! cohort itself, demote the whole thing + ! If the demotion area is less than the cohort area + ! and not trivialy small (larger than precision + ! check), then demote part of it + ! If the demotion area is less than zero or larger than + ! the cohort area within precision checks then FAIL + + whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < + co_area_target_precision ) then + + ! Whole cohort promotion/demotion + cohort%canopy_layer = cohort%canopy_layer + ilyr_change + + elseif( (layer_co(ic)%pd_area < cohort%c_area) .and. & + (layer_co(ic)%pd_area > co_area_target_precision ) ) then + + ! Partial cohort promotion/demotion + + ! Make a copy of the current cohort. The copy and the original + ! conserve total number density of the original. The copy + ! remains in the upper-story. The original is the one + ! demoted to the understory + + + allocate(copyc) + + ! (keep as an example) + ! Initialize running means + !allocate(copyc%tveg_lpa) + !!allocate(copyc%l2fr_ema) + ! Note, no need to give a starter value here, + ! that will be taken care of in copy() + !!call copyc%l2fr_ema%InitRMean(ema_60day) + + ! Initialize the PARTEH object and point to the + ! correct boundary condition fields + copyc%prt => null() + call InitPRTObject(copyc%prt) + + if( hlm_use_planthydro.eq.itrue ) then + call InitHydrCohort(currentSite,copyc) + endif + + call cohort%Copy(copyc) + call copyc%InitPRTBoundaryConditions() + + remainder_area = cohort%c_area - layer_co(ic)%pd_area + copyc%n = cohort%n*remainder_area/cohort%c_area + cohort%n = cohort%n - copyc%n + + ! The copied cohort is the part that remains in-layer + copyc%canopy_layer = cohort%canopy_layer + + ! The original cohort changes layers + cohort%canopy_layer = cohort%canopy_layer + ilyr_change + + call carea_allom(copyc%dbh,copyc%n,Site%spread,copyc%pft, & + copyc%crowndamage, copyc%c_area) + call carea_allom(cohort%dbh,cohort%n,site%spread, & + cohort%pft, cohort%crowndamage, cohort%c_area) + + !----------- Insert copy into linked list ------------------------! + ! Since we are not changing the heights, no sorting necessary + !-----------------------------------------------------------------! + copyc%shorter => ccohort + if(associated(cohort%taller))then + copyc%taller => cohort%taller + cohort%taller%shorter => copyc + else + patch%tallest => copyc + copyc%taller => null() + endif + cohort%taller => copyc + + end if whole_or_part + + ! Part 4: + ! keep track of number and biomass promoted/demoted + + leaf_c = cohort%prt%GetState(leaf_organ,carbon12_element) + store_c = cohort%prt%GetState(store_organ,carbon12_element) + fnrt_c = cohort%prt%GetState(fnrt_organ,carbon12_element) + sapw_c = cohort%prt%GetState(sapw_organ,carbon12_element) + struct_c = cohort%prt%GetState(struct_organ,carbon12_element) + + if(phase==demotion_phase) then + site%demotion_rate(cohort%size_class) = & + site%demotion_rate(cohort%size_class) + cohort%n + site%demotion_carbonflux = site%demotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + else + site%promotion_rate(cohort%size_class) = & + site%promotion_rate(cohort%size_class) + cohort%n + site%promotion_carbonflux = site%promotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + end if + end do ic_loop0 - end if + end associate - return - end subroutine DemoteFromLayer + end subroutine PromoteOrDemote ! ============================================================================================== - - subroutine PromoteIntoLayer(currentSite,currentPatch,i_lyr) - - ! ------------------------------------------------------------------------------------------- - ! Check whether the intended 'full' layers are actually filling all the space. - ! If not, promote some fraction of cohorts upwards. - ! THIS SECTION MIGHT BE TRIGGERED BY A FIRE OR MORTALITY EVENT, FOLLOWED BY A PATCH FUSION, - ! SO THE TOP LAYER IS NO LONGER FULL. - ! ------------------------------------------------------------------------------------------- - - use EDParamsMod, only : ED_val_comp_excln - - ! !ARGUMENTS - type(ed_site_type), intent(inout), target :: currentSite - type(fates_patch_type), intent(inout), target :: currentPatch - integer, intent(in) :: i_lyr ! Current canopy layer of interest - - ! !LOCAL VARIABLES: - type(fates_cohort_type), pointer :: currentCohort - type(fates_cohort_type), pointer :: copyc - type(fates_cohort_type), pointer :: nextc ! the next cohort, or used for looping - ! cohorts against the current - - real(r8) :: scale_factor ! for prob. exclusion - scales weight to a fraction - real(r8) :: scale_factor_min ! "" minimum before exeedance of 1 - real(r8) :: scale_factor_res ! "" applied to residual areas - real(r8) :: area_res ! residual area to demote after weakest cohort hits max - real(r8) :: promote_area - real(r8) :: newarea - real(r8) :: sumweights - real(r8) :: sumequal ! for tied cohorts, the sum of weights in - ! their group - real(r8) :: cc_gain ! cohort crown area gain in promotion (m2) - real(r8) :: arealayer_current ! area (m2) of the current canopy layer - real(r8) :: arealayer_below ! area (m2) of the layer below the current layer - real(r8) :: leaf_c ! leaf carbon [kg] - real(r8) :: fnrt_c ! fineroot carbon [kg] - real(r8) :: sapw_c ! sapwood carbon [kg] - real(r8) :: store_c ! storage carbon [kg] - real(r8) :: struct_c ! structure carbon [kg] - - logical :: tied_size_with_neighbors - real(r8) :: total_crownarea_of_tied_cohorts - - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer_current) - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr+1,arealayer_below) - - - ! how much do we need to gain? - promote_area = currentPatch%area - arealayer_current - - if( promote_area > area_target_precision ) then - - if(arealayer_below <= promote_area ) then - - ! --------------------------------------------------------------------------- - ! Promote all cohorts from layer below if that whole layer has area smaller - ! than the tolerance on the gains needed into current layer - ! --------------------------------------------------------------------------- - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - !look at the cohorts in the canopy layer below... - if(currentCohort%canopy_layer == i_lyr+1)then - - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - currentCohort%canopy_layer = i_lyr - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(currentCohort%size_class) = & - currentSite%promotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * currentCohort%n - - endif - currentCohort => currentCohort%shorter - enddo - - else - - ! --------------------------------------------------------------------------- - ! This is the non-trivial case where the lower layer can accomodate - ! more than what is necessary. - ! --------------------------------------------------------------------------- - - - ! figure out with what weighting we need to promote cohorts. - ! This is the opposite of the demotion weighting... - - sumweights = 0.0_r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage,currentCohort%c_area) - if(currentCohort%canopy_layer == i_lyr+1)then !look at the cohorts in the canopy layer below... - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - ! ------------------------------------------------------------------ - ! Stochastic case, as above (in demotion portion of code) - ! ------------------------------------------------------------------ - - currentCohort%prom_weight = currentCohort%height**ED_val_comp_excln - sumweights = sumweights + currentCohort%prom_weight - else - - ! ------------------------------------------------------------------ - ! Rank ordered deterministic method - ! If there are cohorts that have the exact same height (which is possible, really) - ! we don't want to unilaterally promote/demote one before the others. - ! So we <>mote them as a unit - ! now we need to go through and figure out how many equal-size cohorts there are. - ! then we need to go through, add up the collective crown areas of all equal-sized - ! and equal-canopy-layer cohorts, - ! and then demote from each as if they were a single group - ! ------------------------------------------------------------------ - - total_crownarea_of_tied_cohorts = currentCohort%c_area - tied_size_with_neighbors = .false. - nextc => currentCohort%shorter - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if( nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - tied_size_with_neighbors = .true. - total_crownarea_of_tied_cohorts = & - total_crownarea_of_tied_cohorts + nextc%c_area - end if - else - exit - endif - nextc => nextc%shorter - end do - - if ( tied_size_with_neighbors ) then - - currentCohort%prom_weight = & - max(0.0_r8,min(currentCohort%c_area, & - (currentCohort%c_area/total_crownarea_of_tied_cohorts) * & - (promote_area - sumweights) )) - sumequal = currentCohort%prom_weight - - nextc => currentCohort%shorter - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if (nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - ! now we know the total crown area of all equal-sized, - ! equal-canopy-layer cohorts - nextc%prom_weight = & - max(0.0_r8,min(nextc%c_area, & - (nextc%c_area/total_crownarea_of_tied_cohorts) * & - (promote_area - sumweights) )) - sumequal = sumequal + nextc%prom_weight - end if - else - exit - endif - nextc => nextc%shorter - end do - - ! Update the current cohort pointer to the last similar cohort - ! Its ok if this is not in the right layer - if(associated(nextc))then - currentCohort => nextc%taller - else - currentCohort => currentPatch%shortest - end if - sumweights = sumweights + sumequal - - else - currentCohort%prom_weight = & - max(min(currentCohort%c_area, promote_area - sumweights ), 0._r8) - sumweights = sumweights + currentCohort%prom_weight - - end if - - endif - endif - currentCohort => currentCohort%shorter - enddo !currentCohort - - - ! If this is probabalistic promotion, we need to do a round of normalization. - ! And then a few rounds where we pre-calculate the promotion areas - ! and adjust things if the promoted area wants to be greater than - ! what is available. - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - scale_factor_min = 1.e10_r8 - scale_factor = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - if(currentCohort%canopy_layer == (i_lyr+1) ) then - - currentCohort%prom_weight = currentCohort%prom_weight/sumweights - if( 1._r8/currentCohort%prom_weight < scale_factor_min ) & - scale_factor_min = 1._r8/currentCohort%prom_weight - - scale_factor = scale_factor + currentCohort%prom_weight * currentCohort%c_area - - endif - currentCohort => currentCohort%shorter - enddo - - ! This is the factor by which we need to multiply - ! the demotion probabilities, so the sum result equals - ! the total amount to demote - scale_factor = promote_area/scale_factor - - - if(scale_factor <= scale_factor_min) then - - ! Trivial case, all of the demotion fractions - ! are less than 1. - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1) ) then - currentCohort%prom_weight = currentCohort%c_area * & - currentCohort%prom_weight * scale_factor - - if(debug)then - if((currentCohort%prom_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%prom_weight < 0._r8) ) then - write(fates_log(),*) 'promotion area too big (1)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%prom_weight: ', & - currentCohort%prom_weight - write(fates_log(),*) 'excess: ', & - currentCohort%prom_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - else - - ! Non-trivial case, at least 1 cohort's promotion - ! rate would exceed its area, given the trivial scale factor - - area_res = 0._r8 - scale_factor_res = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1) ) then - area_res = area_res + & - currentCohort%c_area*currentCohort%prom_weight*scale_factor_min - scale_factor_res = scale_factor_res + & - currentCohort%c_area * & - (1._r8 - (currentCohort%prom_weight * scale_factor_min)) - endif - currentCohort => currentCohort%shorter - enddo - - area_res = promote_area - area_res - - scale_factor_res = area_res / scale_factor_res - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1)) then - - currentCohort%prom_weight = currentCohort%c_area * & - (currentCohort%prom_weight * scale_factor_min + & - (1._r8 - (currentCohort%prom_weight*scale_factor_min) ) * & - scale_factor_res) - - if(debug)then - if((currentCohort%prom_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%prom_weight < 0._r8) ) then - write(fates_log(),*) 'promotion area error (2)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%prom_weight: ', & - currentCohort%prom_weight - write(fates_log(),*) 'excess: ', & - currentCohort%prom_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - end if - - end if - - - ! lets perform a check and see if the promotions meet the demand - sumweights = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1)) then - sumweights = sumweights + currentCohort%prom_weight - end if - currentCohort => currentCohort%shorter - end do - - if(debug)then - if (abs(sumweights - promote_area) > area_check_precision ) then - write(fates_log(),*) 'promotions dont add up' - write(fates_log(),*) 'sum promotions: ',sumweights - write(fates_log(),*) 'area needed to be promoted: ',promote_area - write(fates_log(),*) 'excess: ',sumweights - promote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - - !All the trees in this layer need to promote some area upwards... - if( (currentCohort%canopy_layer == i_lyr+1) ) then - - cc_gain = currentCohort%prom_weight - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - if ( (cc_gain-currentCohort%c_area) > -nearzero .and. & - (cc_gain-currentCohort%c_area) < area_target_precision ) then - - currentCohort%canopy_layer = i_lyr - - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(currentCohort%size_class) = & - currentSite%promotion_rate(currentCohort%size_class) + currentCohort%n - - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * currentCohort%n - - elseif ( (cc_gain < currentCohort%c_area) .and. & - (cc_gain > area_target_precision) ) then - - allocate(copyc) - - - !!allocate(copyc%l2fr_ema) - ! Note, no need to give a starter value here, - ! that will be taken care of in copy() - !!call copyc%l2fr_ema%InitRMean(ema_60day) - - ! Initialize the PARTEH object and point to the - ! correct boundary condition fields - copyc%prt => null() - call InitPRTObject(copyc%prt) - - - if( hlm_use_planthydro.eq.itrue ) then - call InitHydrCohort(CurrentSite,copyc) - endif - - ! (keep as an example) - ! Initialize running means - !allocate(copyc%tveg_lpa) - !call copyc%tveg_lpa%InitRMean(ema_lpa,& - ! init_value=currentPatch%tveg_lpa%GetMean()) - - call currentCohort%Copy(copyc) !makes an identical copy... - call copyc%InitPRTBoundaryConditions() - - newarea = currentCohort%c_area - cc_gain !new area of existing cohort - - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - - ! number of individuals in promoted cohort. - copyc%n = currentCohort%n*cc_gain/currentCohort%c_area - - ! number of individuals in cohort remaining in understorey - currentCohort%n = currentCohort%n - copyc%n - - currentCohort%canopy_layer = i_lyr + 1 ! keep current cohort in the understory. - copyc%canopy_layer = i_lyr ! promote copy to the higher canopy layer. - - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(copyc%size_class) = & - currentSite%promotion_rate(copyc%size_class) + copyc%n - - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * copyc%n - - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - call carea_allom(copyc%dbh,copyc%n,currentSite%spread,copyc%pft,& - copyc%crowndamage,copyc%c_area) - - !----------- Insert copy into linked list ------------------------! - copyc%shorter => currentCohort - if(associated(currentCohort%taller))then - copyc%taller => currentCohort%taller - currentCohort%taller%shorter => copyc - else - currentPatch%tallest => copyc - copyc%taller => null() - endif - currentCohort%taller => copyc - - elseif(cc_gain > currentCohort%c_area)then - - write(fates_log(),*) 'more area than the cohort has is being promoted' - write(fates_log(),*) 'loss:',cc_gain - write(fates_log(),*) 'existing area:',currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - - endif - - endif ! if(currentCohort%canopy_layer == i_lyr+1) then - currentCohort => currentCohort%shorter - enddo !currentCohort - - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer_current) - - if ((abs(arealayer_current - currentPatch%area)/arealayer_current > & - area_check_rel_precision ) .or. & - (abs(arealayer_current - currentPatch%area) > area_check_precision) ) then - write(fates_log(),*) 'promotion did not bring area within tolerance' - write(fates_log(),*) 'arealayer:',arealayer_current - write(fates_log(),*) 'patch%area:',currentPatch%area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - end if - - end if - - return - end subroutine PromoteIntoLayer - - ! ============================================================================ - + subroutine canopy_spread( currentSite ) ! ! !DESCRIPTION: @@ -1406,9 +855,9 @@ subroutine canopy_summarization( nsites, sites, bc_in ) call endrun(msg=errMsg(sourcefile, __LINE__)) end if - if (currentPatch%total_canopy_area - currentPatch%area > area_error_1) then + if (currentPatch%total_canopy_area - (1._r8-imperfect_fraction)*currentPatch%area > area_error_1) then write(fates_log(),*) 'too much canopy in summary', s, & - currentPatch%nocomp_pft_label, currentPatch%total_canopy_area - currentPatch%area + currentPatch%nocomp_pft_label, currentPatch%total_canopy_area - (1._r8-imperfect_fraction)*currentPatch%area call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if !sp mode @@ -2279,21 +1728,15 @@ end subroutine UpdateCohortLAI ! =============================================================================================== - function NumPotentialCanopyLayers(currentPatch,site_spread,include_substory) result(z) + function NumCanopyLayers(currentPatch) result(z) ! -------------------------------------------------------------------------------------------- ! Calculate the number of canopy layers in this patch. ! This simple call only determines total layering by querying the cohorts ! which layer they are in, it doesn't do any size evaluation. - ! It may also, optionally, account for the temporary "substory", which is the imaginary - ! layer below the understory which will be needed to temporarily accomodate demotions from - ! the understory in the event the understory has reached maximum allowable area. ! -------------------------------------------------------------------------------------------- - type(fates_patch_type),target :: currentPatch - real(r8),intent(in) :: site_spread - logical :: include_substory - + type(fates_patch_type) :: currentPatch type(fates_cohort_type),pointer :: currentCohort integer :: z @@ -2307,31 +1750,6 @@ function NumPotentialCanopyLayers(currentPatch,site_spread,include_substory) res currentCohort => currentCohort%shorter enddo - if(include_substory)then - arealayer = 0.0 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == z) then - call carea_allom(currentCohort%dbh,currentCohort%n,site_spread,currentCohort%pft, & - currentCohort%crowndamage, c_area) - arealayer = arealayer + c_area - end if - currentCohort => currentCohort%shorter - enddo - - ! Does the bottom layer have more than a full canopy? - ! If so we need to make another layer. - if(arealayer > currentPatch%area)then - z = z + 1 - if(hlm_use_sp.eq.itrue)then - if(debug)then - write(fates_log(),*) 'SPmode, canopy_layer full:',arealayer,currentPatch%area - end if - end if - - endif - end if - - end function NumPotentialCanopyLayers + end function NumCanopyLayers end module EDCanopyStructureMod diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 6d9f8cbcb5..4c65acfb8d 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -339,12 +339,13 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ terminate = itrue termination_type = i_term_mort_type_numdens if ( debug ) then - write(fates_log(),*) 'terminating cohorts 0',currentCohort%n/currentPatch%area,currentCohort%dbh,currentCohort%pft,call_index + write(fates_log(),*) 'terminating cohorts 0',currentCohort%n/currentPatch%area, & + currentCohort%dbh,currentCohort%pft,call_index endif endif ! The rest of these are only allowed if we are not dealing with a recruit (level 2) - if (.not.currentCohort%isnew .and. level == 2) then + if_level_2: if (.not.currentCohort%isnew .and. level == 2) then ! Not enough n or dbh if (currentCohort%n/currentPatch%area <= min_npm2 .or. & ! @@ -353,18 +354,13 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ terminate = itrue termination_type = i_term_mort_type_numdens if ( debug ) then - write(fates_log(),*) 'terminating cohorts 1',currentCohort%n/currentPatch%area,currentCohort%dbh,currentCohort%pft,call_index + write(fates_log(),*) 'terminating cohorts 1', & + currentCohort%n/currentPatch%area,currentCohort%dbh, & + currentCohort%pft,call_index endif endif - ! Outside the maximum canopy layer - if (currentCohort%canopy_layer > nclmax ) then - terminate = itrue - termination_type = i_term_mort_type_canlev - if ( debug ) then - write(fates_log(),*) 'terminating cohorts 2', currentCohort%canopy_layer,currentCohort%pft,call_index - endif - endif + ! live biomass pools are terminally depleted if ( ( sapw_c+leaf_c+fnrt_c ) < 1e-10_r8 .or. & @@ -386,8 +382,18 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ struct_c,sapw_c,leaf_c,fnrt_c,store_c,currentCohort%pft,call_index endif - endif - endif ! if (.not.currentCohort%isnew .and. level == 2) then + endif + + end if if_level_2 + + ! Outside the maximum canopy layer + if (currentCohort%canopy_layer > nclmax .and. level == 3) then + terminate = itrue + termination_type = i_term_mort_type_canlev + if ( debug ) then + write(fates_log(),*) 'terminating cohorts 2', currentCohort%canopy_layer,currentCohort%pft,call_index + endif + endif if (terminate == itrue) then call terminate_cohort(currentSite, currentPatch, currentCohort, bc_in, termination_type) diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 9b3b9ef919..9d84b4ce53 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -21,7 +21,7 @@ module FatesPatchMod use PRTGenericMod, only : struct_organ, leaf_organ, sapw_organ use PRTParametersMod, only : prt_params use FatesConstantsMod, only : nocomp_bareground - use EDParamsMod, only : nlevleaf, nclmax, maxpft + use EDParamsMod, only : nlevleaf, nclmax, maxpft,max_cohort_per_patch use FatesConstantsMod, only : n_dbh_bins, n_dist_types use FatesConstantsMod, only : t_water_freeze_k_1atm use FatesRunningMeanMod, only : ema_24hr, fixed_24hr, ema_lpa, ema_longterm @@ -41,6 +41,26 @@ module FatesPatchMod ! for error message writing character(len=*), parameter :: sourcefile = __FILE__ + type :: fates_cohort_vec_type + + ! This is a scratch array for cohort pointers + ! this is useful if you want to loop over a sparse subset + ! of fates cohorts over and over again, allowing + ! you to iterate them in a do loop + + type(fates_cohort_type), pointer :: p => null() + + ! This is the area of the cohort (less than or equal to cohort%carea) + ! that will be promoted or demoted, ie promoted/demoted crown area + ! units [m2/site] or [m2/ha] (same as the patch area and crown area) + ! We track it here because we construct the cohort list for specific + ! canopy layers + + real(r8) :: pd_area + + end type fates_cohort_vec_type + + type, public :: fates_patch_type ! POINTERS @@ -48,7 +68,8 @@ module FatesPatchMod type (fates_cohort_type), pointer :: shortest => null() ! pointer to patch's shortest cohort type (fates_patch_type), pointer :: older => null() ! pointer to next older patch type (fates_patch_type), pointer :: younger => null() ! pointer to next younger patch - + type (fates_cohort_vec_type), pointer :: co_scr(:) ! Scratch vector of cohort properties + !--------------------------------------------------------------------------- ! INDICES @@ -269,7 +290,8 @@ subroutine Init(this, num_swb, num_levsoil) allocate(this%sabs_dir(num_swb)) allocate(this%sabs_dif(num_swb)) allocate(this%fragmentation_scaler(num_levsoil)) - + allocate(this%co_scr(max_cohort_per_patch)) + ! initialize all values to nan call this%NanValues() @@ -878,6 +900,7 @@ subroutine FreeMemory(this, regeneration_model, numpft) this%sabs_dir, & this%sabs_dif, & this%fragmentation_scaler, & + this%co_scr, & stat=istat, errmsg=smsg) ! These arrays are allocated via a call from EDCanopyStructureMod diff --git a/main/EDParamsMod.F90 b/main/EDParamsMod.F90 index 63d8c0aaf7..420b34999e 100644 --- a/main/EDParamsMod.F90 +++ b/main/EDParamsMod.F90 @@ -44,7 +44,7 @@ module EDParamsMod !moving average of par at the seedling layer used to !calculate seedling to sapling transition rates real(r8),protected, public :: fates_mortality_disturbance_fraction ! the fraction of canopy mortality that results in disturbance - real(r8),protected, public :: ED_val_comp_excln ! weighting factor for canopy layer exclusion and promotion + real(r8),protected, public :: comp_excln_exp ! weighting factor (exponent) for canopy layer exclusion and promotion real(r8),protected, public :: ED_val_vai_top_bin_width ! width in VAI units of uppermost leaf+stem layer scattering element real(r8),protected, public :: ED_val_vai_width_increase_factor ! factor by which each leaf+stem scattering element increases in VAI width real(r8),protected, public :: ED_val_nignitions ! number of annual ignitions per square km @@ -285,7 +285,7 @@ subroutine FatesParamsInit() sdlng2sap_par_timescale = nan photo_temp_acclim_thome_time = nan fates_mortality_disturbance_fraction = nan - ED_val_comp_excln = nan + comp_excln_exp = nan ED_val_vai_top_bin_width = nan ED_val_vai_width_increase_factor = nan ED_val_nignitions = nan @@ -592,7 +592,7 @@ subroutine FatesReceiveParams(fates_params) data=fates_mortality_disturbance_fraction) call fates_params%RetrieveParameter(name=ED_name_comp_excln, & - data=ED_val_comp_excln) + data=comp_excln_exp) call fates_params%RetrieveParameter(name=ED_name_vai_top_bin_width, & data=ED_val_vai_top_bin_width) @@ -807,7 +807,7 @@ subroutine FatesReportParams(is_master) write(fates_log(),fmt0) 'photo_temp_acclim_thome_time (years) = ',photo_temp_acclim_thome_time write(fates_log(),fmti) 'hydr_htftype_node = ',hydr_htftype_node write(fates_log(),fmt0) 'fates_mortality_disturbance_fraction = ',fates_mortality_disturbance_fraction - write(fates_log(),fmt0) 'ED_val_comp_excln = ',ED_val_comp_excln + write(fates_log(),fmt0) 'comp_excln_exp = ',comp_excln_exp write(fates_log(),fmt0) 'ED_val_vai_top_bin_width = ',ED_val_vai_top_bin_width write(fates_log(),fmt0) 'ED_val_vai_width_increase_factor = ',ED_val_vai_width_increase_factor write(fates_log(),fmt0) 'ED_val_nignitions = ',ED_val_nignitions From 0e3bdd458c257117efbfdffc77776868dcfd89d3 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 13 Jun 2025 09:54:37 -0700 Subject: [PATCH 092/194] small fixes to canopy structure --- biogeochem/EDCanopyStructureMod.F90 | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 707dfb7f13..e7c7e20d4a 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -101,13 +101,13 @@ module EDCanopyStructureMod logical, parameter :: preserve_b4b = .true. - ! If we want to allow some degree of imperfection in canopy closer, we would - ! add it here + ! If we want to allow some degree of imperfection + ! in canopy closure we would add it here real(r8), parameter :: imperfect_fraction = 0._r8 ! 10/30/09: Created by Rosie Fisher - ! 2017/2018: Modifications and updates by Ryan Knox + ! 2017/2018/2025: Modifications and updates by Ryan Knox ! ============================================================================ contains @@ -171,6 +171,9 @@ subroutine canopy_structure( currentSite , bc_in ) real(r8) :: arealayer(nclmax+5) ! Amount of plant area currently in each canopy layer integer :: patch_area_counter ! count iterations used to solve canopy areas logical :: area_not_balanced ! logical controlling if the patch layer areas + real(r8) :: target_area ! Canopy area that is either in excess/defiency + ! that is slated for demotion/promotion from/into layer + ! have successfully been redistributed integer :: return_code ! math checks on variables will return>0 if problems exist ! We only iterate because of possible imprecisions generated by the cohort @@ -590,12 +593,12 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) cohort => layer_co(ic)%p - ! If the demotion area is the same area as the - ! cohort itself, demote the whole thing - ! If the demotion area is less than the cohort area + ! If the dem/prom area is the same area as the + ! cohort itself, move the whole thing + ! If the dem/prom area is less than the cohort area ! and not trivialy small (larger than precision - ! check), then demote part of it - ! If the demotion area is less than zero or larger than + ! check), then split it and move part of it + ! If the dem/prom area is less than zero or larger than ! the cohort area within precision checks then FAIL whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < @@ -605,12 +608,12 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) cohort%canopy_layer = cohort%canopy_layer + ilyr_change elseif( (layer_co(ic)%pd_area < cohort%c_area) .and. & - (layer_co(ic)%pd_area > co_area_target_precision ) ) then + (layer_co(ic)%pd_area > co_area_target_precision ) ) then ! Partial cohort promotion/demotion ! Make a copy of the current cohort. The copy and the original - ! conserve total number density of the original. The copy + ! conserve total number density. The copy ! remains in the upper-story. The original is the one ! demoted to the understory @@ -647,7 +650,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! The original cohort changes layers cohort%canopy_layer = cohort%canopy_layer + ilyr_change - call carea_allom(copyc%dbh,copyc%n,Site%spread,copyc%pft, & + call carea_allom(copyc%dbh,copyc%n,site%spread,copyc%pft, & copyc%crowndamage, copyc%c_area) call carea_allom(cohort%dbh,cohort%n,site%spread, & cohort%pft, cohort%crowndamage, cohort%c_area) From 527fbee3f252467886238d214aab83b6260378ad Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 13 Jun 2025 10:21:24 -0700 Subject: [PATCH 093/194] added argument to sorting that turns it into a check instead of an execution --- biogeochem/EDCanopyStructureMod.F90 | 4 ++++ biogeochem/EDCohortDynamicsMod.F90 | 2 +- biogeochem/EDPatchDynamicsMod.F90 | 10 ++++----- biogeochem/FatesPatchMod.F90 | 21 +++++++++++++++++- main/EDInitMod.F90 | 2 +- main/EDMainMod.F90 | 2 +- main/FatesInventoryInitMod.F90 | 2 +- .../sort_cohorts_test/test_SortCohorts.pf | 22 +++++++++---------- 8 files changed, 44 insertions(+), 21 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index e7c7e20d4a..b47c2b613f 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -199,6 +199,10 @@ subroutine canopy_structure( currentSite , bc_in ) ! do while (associated(currentPatch)) ! Patch loop + ! Make sure we are sorted + call currentPatch%SortCohorts(check_order=.true.) + + ! ------------------------------------------------------------------------------ ! Perform numerical checks on some cohort and patch structures ! ------------------------------------------------------------------------------ diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 4c65acfb8d..87e94742f7 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -1218,7 +1218,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) endif ! patch. if (fusion_took_place == 1) then ! if fusion(s) occured sort cohorts - call currentPatch%SortCohorts() + call currentPatch%SortCohorts(check_order=.false.) call currentPatch%ValidateCohorts() endif diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index b1fc9af66d..27d6df863d 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -1256,7 +1256,7 @@ subroutine spawn_patches( currentSite, bc_in) enddo cohortloop call newPatch%ValidateCohorts() - call currentPatch%SortCohorts() + call currentPatch%SortCohorts(check_order=.false.) call currentPatch%ValidateCohorts() !update area of donor patch @@ -1288,7 +1288,7 @@ subroutine spawn_patches( currentSite, bc_in) call terminate_cohorts(currentSite, currentPatch, 1,16,bc_in) call fuse_cohorts(currentSite,currentPatch, bc_in) call terminate_cohorts(currentSite, currentPatch, 2,16,bc_in) - call currentPatch%SortCohorts() + call currentPatch%SortCohorts(check_order=.false.) call currentPatch%ValidateCohorts() end if areadis_gt_zero_if ! if ( newPatch%area > nearzero ) then @@ -1316,7 +1316,7 @@ subroutine spawn_patches( currentSite, bc_in) call terminate_cohorts(currentSite, newPatch, 1,17, bc_in) call fuse_cohorts(currentSite,newPatch, bc_in) call terminate_cohorts(currentSite, newPatch, 2,17, bc_in) - call newPatch%SortCohorts() + call newPatch%SortCohorts(check_order=.false.) call newPatch%ValidateCohorts() endif @@ -1721,7 +1721,7 @@ subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, a enddo ! currentCohort call new_patch%ValidateCohorts() - call currentPatch%SortCohorts() + call currentPatch%SortCohorts(check_order=.false.) call currentPatch%ValidateCohorts() !update area of donor patch @@ -3023,7 +3023,7 @@ subroutine fuse_patches( csite, bc_in ) tmpptr => currentPatch%older call fuse_2_patches(csite, currentPatch, tpp) call fuse_cohorts(csite,tpp, bc_in) - call tpp%SortCohorts() + call tpp%SortCohorts(check_order=.false.) call tpp%ValidateCohorts() currentPatch => tmpptr diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 9d84b4ce53..086b09a6cf 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -1164,7 +1164,7 @@ end subroutine CountCohorts !=========================================================================== - subroutine SortCohorts(this) + subroutine SortCohorts(this,check_order) ! ! DESCRIPTION: sort cohorts in patch's linked list ! uses insertion sort to build a new list @@ -1172,6 +1172,8 @@ subroutine SortCohorts(this) ! ARGUMENTS: class(fates_patch_type), intent(inout), target :: this ! patch + + logical,intent(in) :: check_order ! LOCALS: type(fates_cohort_type), pointer :: currentCohort @@ -1189,6 +1191,23 @@ subroutine SortCohorts(this) ! hold on to current linked list so we don't lose it currentCohort => this%shortest + + if(check_order)then + do while (associated(currentCohort)) + if( associated(currentCohort%taller)) then + if(currentCohort%height > currentCohort%taller%height)then + write(fates_log(),*) 'Cohort sort checking has failed,' + write(fates_log(),*) 'they are not in height order:' + write(fates_log(),*) 'current: ',currentCohort%height + write(fates_log(),*) 'taller: ',currentCohort%taller%height + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end if + currentCohort => currentCohort%taller + end do + return + end if + ! reset the current list: we'll build it incrementally this%shortest => null() diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 625e65fabc..6db044723b 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -1332,7 +1332,7 @@ subroutine init_cohorts(site_in, patch_in, bc_in) if (hlm_use_sp == ifalse) then call fuse_cohorts(site_in, patch_in,bc_in) - call patch_in%SortCohorts() + call patch_in%SortCohorts(check_order=.false.) end if call patch_in%ValidateCohorts() diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 979aa2960d..0fa0040bdc 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -261,7 +261,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) do while (associated(currentPatch)) ! puts cohorts in right order - call currentPatch%SortCohorts() + call currentPatch%SortCohorts(check_order=.false.) ! kills cohorts that are too few call terminate_cohorts(currentSite, currentPatch, 1, 10, bc_in ) diff --git a/main/FatesInventoryInitMod.F90 b/main/FatesInventoryInitMod.F90 index b5fe1acfe9..304510e23d 100644 --- a/main/FatesInventoryInitMod.F90 +++ b/main/FatesInventoryInitMod.F90 @@ -428,7 +428,7 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) ! Perform Cohort Fusion call fuse_cohorts(sites(s), currentpatch,bc_in(s)) - call currentpatch%SortCohorts() + call currentpatch%SortCohorts(check_order=.false.) ! This calculates %num_cohorts call currentPatch%CountCohorts() diff --git a/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf b/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf index 9ee33910f3..366213f405 100644 --- a/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf +++ b/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf @@ -24,8 +24,8 @@ module test_SortCohorts class(TestSortCohorts), intent(inout) :: this ! test object type(fates_patch_type) :: patch ! patch object - ! sort cohorts - should pass - call patch%SortCohorts() + ! sort cohorts - should pass - the argument + call patch%SortCohorts(check_order=.false.) end subroutine EmptyList_SortCohorts_Passes @@ -42,7 +42,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = 1 @@ -68,7 +68,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = size(heights) @@ -96,7 +96,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = 1 @@ -123,7 +123,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = size(heights) @@ -151,7 +151,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! check backwards and forwards cohort => patch%shortest @@ -186,7 +186,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights, dbhs=dbhs) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = 1 @@ -223,7 +223,7 @@ module test_SortCohorts call CreateTestPatchList(patch, heights, dbhs=dbhs) ! sort cohorts - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) ! test that the order is correct i = 1 @@ -266,7 +266,7 @@ module test_SortCohorts cohort3%shorter => cohort2 ! should fail - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) @assertExceptionRaised(expected_msg) ! try the opposite @@ -274,7 +274,7 @@ module test_SortCohorts patch%tallest => cohort3 ! should also fail - call patch%SortCohorts() + call patch%SortCohorts(check_order=.false.) @assertExceptionRaised(expected_msg) end subroutine SortCohorts_InconsistentListState_Errors From 6cc31410475215a97187503b36c473f212c4314a Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 14:46:14 -0600 Subject: [PATCH 094/194] Functional test fix: DATM file now found when using -r. --- testing/functional_class.py | 12 ++++++++++-- .../functional_testing/allometry/allometry_test.py | 1 + testing/functional_testing/fire/fuel/fuel_test.py | 1 + testing/functional_testing/fire/ros/ros_test.py | 1 + .../functional_testing/math_utils/math_utils_test.py | 1 + testing/functional_testing/patch/patch_test.py | 1 + testing/functional_tests.cfg | 7 ++++++- testing/run_functional_tests.py | 5 ++++- 8 files changed, 25 insertions(+), 4 deletions(-) diff --git a/testing/functional_class.py b/testing/functional_class.py index 6ca085ef2c..c3ea2319cc 100644 --- a/testing/functional_class.py +++ b/testing/functional_class.py @@ -1,3 +1,4 @@ +import os from abc import ABC, abstractmethod from utils import str_to_bool, str_to_list @@ -5,15 +6,22 @@ class FunctionalTest(ABC): """Class for running FATES functional tests""" def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, - use_param_file:str, other_args:str): + use_param_file:str, datm_file:str, other_args:str): self.name = name self.test_dir = test_dir self.test_exe = test_exe self.out_file = out_file self.use_param_file = str_to_bool(use_param_file) + self.datm_file = None self.other_args = str_to_list(other_args) self.plot = False - + + # Check that datm exists and save its absolute path + if datm_file: + if not os.path.exists(datm_file): + raise FileNotFoundError(f"datm_file not found: '{datm_file}'") + self.datm_file = os.path.abspath(datm_file) + @abstractmethod def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str): pass diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index bb24ab3729..fa41e3d4ae 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -23,6 +23,7 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], + test_dict["datm_file"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/fuel/fuel_test.py b/testing/functional_testing/fire/fuel/fuel_test.py index b9cd151621..e9a74cd5fb 100644 --- a/testing/functional_testing/fire/fuel/fuel_test.py +++ b/testing/functional_testing/fire/fuel/fuel_test.py @@ -20,6 +20,7 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], + test_dict["datm_file"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/ros/ros_test.py b/testing/functional_testing/fire/ros/ros_test.py index e845040fdb..1019f02ac5 100644 --- a/testing/functional_testing/fire/ros/ros_test.py +++ b/testing/functional_testing/fire/ros/ros_test.py @@ -26,6 +26,7 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], + test_dict["datm_file"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py index 579838df18..70abd58891 100644 --- a/testing/functional_testing/math_utils/math_utils_test.py +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -22,6 +22,7 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], + test_dict["datm_file"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/patch/patch_test.py b/testing/functional_testing/patch/patch_test.py index 7d4fec3e8d..8fd150a08e 100644 --- a/testing/functional_testing/patch/patch_test.py +++ b/testing/functional_testing/patch/patch_test.py @@ -23,6 +23,7 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], + test_dict["datm_file"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_tests.cfg b/testing/functional_tests.cfg index 98d8448e42..e3b28f7e44 100644 --- a/testing/functional_tests.cfg +++ b/testing/functional_tests.cfg @@ -3,6 +3,7 @@ test_dir = fates_allom_ftest test_exe = FATES_allom_exe out_file = allometry_out.nc use_param_file = True +datm_file = other_args = [] [quadratic] @@ -10,6 +11,7 @@ test_dir = fates_math_ftest test_exe = FATES_math_exe out_file = quad_out.nc use_param_file = False +datm_file = other_args = [] [fuel] @@ -17,13 +19,15 @@ test_dir = fates_fuel_ftest test_exe = FATES_fuel_exe out_file = fuel_out.nc use_param_file = True -other_args = ['../testing/test_data/BONA_datm.nc'] +datm_file = ../testing/test_data/BONA_datm.nc +other_args = [] [ros] test_dir = fates_ros_ftest test_exe = FATES_ros_exe out_file = ros_out.nc use_param_file = True +datm_file = other_args = [] [patch] @@ -31,4 +35,5 @@ test_dir = fates_patch_ftest test_exe = FATES_patch_exe out_file = None use_param_file = True +datm_file = other_args = [] diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 105c22b06a..9eeca3a194 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -295,8 +295,11 @@ def run_functional_tests( if run_executables: print("Running executables") for _, test in test_dict.items(): - # prepend parameter file (if required) to argument list args = test.other_args + # prepend datm file (if required) to argument list + if test.datm_file: + args.insert(0, test.datm_file) + # prepend parameter file (if required) to argument list if test.use_param_file: args.insert(0, param_file) # run From a2c2a2d8819776ed82f0c6c4f19a022d2e38156f Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 15:08:53 -0600 Subject: [PATCH 095/194] Functional tests now work if called from somewhere other than testing/. --- testing/run_functional_tests.py | 12 +++++++++--- testing/utils.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 9eeca3a194..35d816ca32 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -42,9 +42,15 @@ from CIME.utils import run_cmd_no_fail # constants for this script -_DEFAULT_CONFIG_FILE = "functional_tests.cfg" -_DEFAULT_CDL_PATH = os.path.abspath("../parameter_files/fates_params_default.cdl") -_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../") +_FILE_DIR = os.path.dirname(__file__) +_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "functional_tests.cfg") +_DEFAULT_CDL_PATH = os.path.abspath(os.path.join( + _FILE_DIR, + os.pardir, + "parameter_files", + "fates_params_default.cdl", +)) +_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir) _TEST_SUB_DIR = "testing" diff --git a/testing/utils.py b/testing/utils.py index cd1973b5b7..57b49d79fe 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -123,6 +123,27 @@ def get_color_palette(number: int) -> list: return colors[:number] +def get_abspath_from_config_file(relative_path, config_file): + """ + Gets the absolute path of a file relative to the config file where it was defined. + + Args: + relative_path: The path to the target file, relative to the base file. + config_file: The path to the config file. + + Returns: + The absolute path of the target file. + """ + + # Do nothing if it's already a absolute path + if os.path.isabs(relative_path): + return relative_path + + base_dir = os.path.dirname(os.path.abspath(config_file)) + absolute_path = os.path.abspath(os.path.join(base_dir, relative_path)) + return absolute_path + + def config_to_dict(config_file: str) -> dict: """Convert a config file to a python dictionary @@ -139,6 +160,9 @@ def config_to_dict(config_file: str) -> dict: if not os.path.isfile(config_file): raise RuntimeError(f"config_file is a directory: '{config_file}'") + # Define list of config file options that we expect to be paths + options_that_are_paths = ["datm_file"] + config = configparser.ConfigParser() config.read(config_file) @@ -146,7 +170,14 @@ def config_to_dict(config_file: str) -> dict: for section in config.sections(): dictionary[section] = {} for option in config.options(section): - dictionary[section][option] = config.get(section, option) + value = config.get(section, option) + + # If the option is one that we expect to be a path, ensure it's an absolute path. + if option in options_that_are_paths: + value = get_abspath_from_config_file(value, config_file) + + # Save value to dictionary + dictionary[section][option] = value return dictionary From c0af35597bac291aba738a1d86a9325a289b0084 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 15:18:28 -0600 Subject: [PATCH 096/194] Functional tests: Simplify an error. --- testing/run_functional_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 35d816ca32..d2417a8dcf 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -202,7 +202,7 @@ def check_param_file(param_file): None, "Must supply parameter file with .cdl or .nc ending." ) if not os.path.isfile(param_file): - raise argparse.ArgumentError(None, f"Cannot find file {param_file}.") + raise FileNotFoundError(param_file) def check_build_dir(build_dir, test_dict): From 08c1dbc04a64f22cf28b278022df7a1bec329e42 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 15:22:03 -0600 Subject: [PATCH 097/194] run_functional_tests.py: Add optional --config-file argument. --- testing/run_functional_tests.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index d2417a8dcf..433111ebdc 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -80,6 +80,13 @@ def commandline_args(): "parameter_files directory.\n", ) + parser.add_argument( + "--config-file", + type=str, + default=_DEFAULT_CONFIG_FILE, + help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'", + ) + parser.add_argument( "-b", "--build-dir", @@ -404,13 +411,13 @@ def main(): Reads in command-line arguments and then runs the tests. """ - full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) - subclasses = FunctionalTest.__subclasses__() - args = commandline_args() + + full_test_dict = config_to_dict(args.config_file) config_dict = parse_test_list(full_test_dict, args.test_list) test_dict = {} + subclasses = FunctionalTest.__subclasses__() for name in config_dict.keys(): test_class = list(filter(lambda subclass: subclass.name == name, subclasses))[ 0 From a80dd9e3647513d7d867515ba8e20deed2d4ba29 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 15:54:50 -0600 Subject: [PATCH 098/194] Functional tests: Error if Fortran executable fails. --- testing/run_functional_tests.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 433111ebdc..7186f76aa2 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -28,6 +28,7 @@ """ import os import argparse +import subprocess import matplotlib.pyplot as plt from build_fortran_tests import build_tests, build_exists @@ -39,7 +40,7 @@ add_cime_lib_to_path() -from CIME.utils import run_cmd_no_fail +from CIME.utils import run_cmd # constants for this script _FILE_DIR = os.path.dirname(__file__) @@ -402,7 +403,11 @@ def run_fortran_exectuables(build_dir, test_dir, test_exe, run_dir, args): run_command.extend(args) os.chdir(run_dir) - out = run_cmd_no_fail(" ".join(run_command), combine_output=True) + cmd = " ".join(run_command) + stat, out, _ = run_cmd(cmd, combine_output=True) + if stat: + print(out) + raise subprocess.CalledProcessError(stat, cmd, out) print(out) From d34666cb0becc2ac9e6f19191681a6331b1170a2 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 16:08:54 -0600 Subject: [PATCH 099/194] Functional tests: Fortran now errors instead of just stopping. --- .../functional_testing/fire/shr/SyntheticFuelModels.F90 | 2 +- testing/testing_shr/FatesArgumentUtils.F90 | 2 +- testing/testing_shr/FatesFactoryMod.F90 | 2 +- testing/testing_shr/FatesUnitTestIOMod.F90 | 8 ++++---- testing/testing_shr/FatesUnitTestParamReaderMod.F90 | 2 +- testing/testing_shr/SyntheticPatchTypes.F90 | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 b/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 index ce1c8e85e2..aab21bab41 100644 --- a/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 +++ b/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 @@ -178,7 +178,7 @@ integer function FuelModelPosition(this, fuel_model_index) end if end do write(*, '(a, i2, a)') "Cannot find the fuel model index ", fuel_model_index, "." - stop + call abort() end function FuelModelPosition diff --git a/testing/testing_shr/FatesArgumentUtils.F90 b/testing/testing_shr/FatesArgumentUtils.F90 index ed247fa157..2bdbf825d3 100644 --- a/testing/testing_shr/FatesArgumentUtils.F90 +++ b/testing/testing_shr/FatesArgumentUtils.F90 @@ -24,7 +24,7 @@ function command_line_arg(arg_position) if (n_args < arg_position) then write(*, '(a, i2, a, i2)') "Incorrect number of arguments: ", n_args, ". Should be at least", arg_position, "." - stop + call abort() end if call get_command_argument(arg_position, length=arglen) diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index a1d031f717..41a73103d8 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -520,7 +520,7 @@ subroutine CreateTestPatchList(patch, heights, dbhs) if (present(dbhs)) then if (size(heights) /= size(dbhs)) then write(*, '(a)') "Size of heights array must match size of dbh array." - stop + call abort() end if end if diff --git a/testing/testing_shr/FatesUnitTestIOMod.F90 b/testing/testing_shr/FatesUnitTestIOMod.F90 index 20dd4f198e..51ced8b45a 100644 --- a/testing/testing_shr/FatesUnitTestIOMod.F90 +++ b/testing/testing_shr/FatesUnitTestIOMod.F90 @@ -106,7 +106,7 @@ subroutine Check(status) if (status /= nf90_noerr) then write(*,*) trim(nf90_strerror(status)) - stop + call abort() end if end subroutine Check @@ -134,11 +134,11 @@ subroutine OpenNCFile(nc_file, ncid, fmode) call Check(nf90_create(trim(nc_file), NF90_CLOBBER, ncid)) case DEFAULT write(*,*) 'Need to specify read, write, or readwrite' - stop + call abort() end select else write(*,*) 'Problem reading file' - stop + call abort() end if end subroutine OpenNCFile @@ -480,7 +480,7 @@ subroutine RegisterVar(ncid, var_name, dimID, type, att_names, atts, num_atts, v nc_type = NF90_CHAR else write(*, *) "Must pick correct type" - stop + call abort() end if call Check(nf90_def_var(ncid, var_name, nc_type, dimID, varID)) diff --git a/testing/testing_shr/FatesUnitTestParamReaderMod.F90 b/testing/testing_shr/FatesUnitTestParamReaderMod.F90 index 2a4fb13cd8..d8b655136b 100644 --- a/testing/testing_shr/FatesUnitTestParamReaderMod.F90 +++ b/testing/testing_shr/FatesUnitTestParamReaderMod.F90 @@ -89,7 +89,7 @@ subroutine ReadParameters(this, fates_params) case default write(*, '(a,a)') 'dimension shape:', dimension_shape write(*, '(a)') 'unsupported number of dimensions reading parameters.' - stop + call abort() end select end do diff --git a/testing/testing_shr/SyntheticPatchTypes.F90 b/testing/testing_shr/SyntheticPatchTypes.F90 index 6094f0c6da..64747e5b6d 100644 --- a/testing/testing_shr/SyntheticPatchTypes.F90 +++ b/testing/testing_shr/SyntheticPatchTypes.F90 @@ -163,7 +163,7 @@ integer function PatchDataPosition(this, patch_id, patch_name) ! can't supply both if (present(patch_id) .and. present(patch_name)) then write(*, '(a)') "Can only supply either a patch_id or a patch_name - not both" - stop + call abort() end if do i = 1, this%num_patches @@ -179,11 +179,11 @@ integer function PatchDataPosition(this, patch_id, patch_name) end if else write(*, '(a)') "Must supply either a patch_id or a patch_name." - stop + call abort() end if end do write(*, '(a)') "Cannot find the synthetic patch type supplied" - stop + call abort() end function PatchDataPosition From 3b4732335bdd771f8781205637a3769728c27fb5 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 13 Jun 2025 16:34:48 -0600 Subject: [PATCH 100/194] Functional tests: Download datm_file if needed. KLUDGE! This is a kludge because it (a) downloads the datm_file from my Dropbox and (b) requires an extra line in each config file section. Revert this change once BONA_datm.nc is on Git LFS and that's working consistently. --- testing/functional_class.py | 13 ++++++++++--- .../functional_testing/allometry/allometry_test.py | 1 + testing/functional_testing/fire/fuel/fuel_test.py | 1 + testing/functional_testing/fire/ros/ros_test.py | 1 + .../math_utils/math_utils_test.py | 1 + testing/functional_testing/patch/patch_test.py | 1 + testing/functional_tests.cfg | 5 +++++ 7 files changed, 20 insertions(+), 3 deletions(-) diff --git a/testing/functional_class.py b/testing/functional_class.py index c3ea2319cc..50da8e6c5b 100644 --- a/testing/functional_class.py +++ b/testing/functional_class.py @@ -1,4 +1,5 @@ import os +import urllib.request from abc import ABC, abstractmethod from utils import str_to_bool, str_to_list @@ -6,7 +7,7 @@ class FunctionalTest(ABC): """Class for running FATES functional tests""" def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, - use_param_file:str, datm_file:str, other_args:str): + use_param_file:str, datm_file:str, datm_file_url:str, other_args:str): self.name = name self.test_dir = test_dir self.test_exe = test_exe @@ -18,9 +19,15 @@ def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, # Check that datm exists and save its absolute path if datm_file: - if not os.path.exists(datm_file): - raise FileNotFoundError(f"datm_file not found: '{datm_file}'") self.datm_file = os.path.abspath(datm_file) + if not os.path.exists(self.datm_file): + if not datm_file_url: + raise FileNotFoundError(f"datm_file not found: '{self.datm_file}'") + datm_file_dir = os.path.dirname(self.datm_file) + if not os.path.isdir(datm_file_dir): + os.makedirs(datm_file_dir) + print(f"Downloading datm_file from {datm_file_url}") + urllib.request.urlretrieve(datm_file_url, self.datm_file) @abstractmethod def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str): diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index fa41e3d4ae..d75fd8f279 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -24,6 +24,7 @@ def __init__(self, test_dict): test_dict["out_file"], test_dict["use_param_file"], test_dict["datm_file"], + test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/fuel/fuel_test.py b/testing/functional_testing/fire/fuel/fuel_test.py index e9a74cd5fb..9a6ec034bc 100644 --- a/testing/functional_testing/fire/fuel/fuel_test.py +++ b/testing/functional_testing/fire/fuel/fuel_test.py @@ -21,6 +21,7 @@ def __init__(self, test_dict): test_dict["out_file"], test_dict["use_param_file"], test_dict["datm_file"], + test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/ros/ros_test.py b/testing/functional_testing/fire/ros/ros_test.py index 1019f02ac5..8bdec046d8 100644 --- a/testing/functional_testing/fire/ros/ros_test.py +++ b/testing/functional_testing/fire/ros/ros_test.py @@ -27,6 +27,7 @@ def __init__(self, test_dict): test_dict["out_file"], test_dict["use_param_file"], test_dict["datm_file"], + test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py index 70abd58891..b9c82fae83 100644 --- a/testing/functional_testing/math_utils/math_utils_test.py +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -23,6 +23,7 @@ def __init__(self, test_dict): test_dict["out_file"], test_dict["use_param_file"], test_dict["datm_file"], + test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/patch/patch_test.py b/testing/functional_testing/patch/patch_test.py index 8fd150a08e..c0201b6a2b 100644 --- a/testing/functional_testing/patch/patch_test.py +++ b/testing/functional_testing/patch/patch_test.py @@ -24,6 +24,7 @@ def __init__(self, test_dict): test_dict["out_file"], test_dict["use_param_file"], test_dict["datm_file"], + test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_tests.cfg b/testing/functional_tests.cfg index e3b28f7e44..d5bc1a3c23 100644 --- a/testing/functional_tests.cfg +++ b/testing/functional_tests.cfg @@ -4,6 +4,7 @@ test_exe = FATES_allom_exe out_file = allometry_out.nc use_param_file = True datm_file = +datm_file_url = other_args = [] [quadratic] @@ -12,6 +13,7 @@ test_exe = FATES_math_exe out_file = quad_out.nc use_param_file = False datm_file = +datm_file_url = other_args = [] [fuel] @@ -20,6 +22,7 @@ test_exe = FATES_fuel_exe out_file = fuel_out.nc use_param_file = True datm_file = ../testing/test_data/BONA_datm.nc +datm_file_url = https://www.dropbox.com/scl/fi/l7ik0xhnww3snlk2lqngr/BONA_datm.nc?rlkey=15kwixoofokyyj936xkxmfq8t&e=1&dl=1 other_args = [] [ros] @@ -28,6 +31,7 @@ test_exe = FATES_ros_exe out_file = ros_out.nc use_param_file = True datm_file = +datm_file_url = other_args = [] [patch] @@ -36,4 +40,5 @@ test_exe = FATES_patch_exe out_file = None use_param_file = True datm_file = +datm_file_url = other_args = [] From d70ccca06cb260a4aa8b30001b3939f68aa4ab3e Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Sat, 14 Jun 2025 14:45:33 -0600 Subject: [PATCH 101/194] minor fixes and corrections to promotion/demotion refactor --- biogeochem/EDCanopyStructureMod.F90 | 30 +++++++++++++++-------------- main/FatesHistoryInterfaceMod.F90 | 8 ++++---- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index fb237fa4e7..0484fc9f6b 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -28,6 +28,7 @@ module EDCanopyStructureMod use EDParamsMod , only : nclmax use EDParamsMod , only : nlevleaf use EDParamsMod , only : GetNVegLayers + use EDParamsMod , only : comp_excln_exp use EDtypesMod , only : AREA use EDLoggingMortalityMod , only : UpdateHarvestC use FatesGlobals , only : endrun => fates_endrun @@ -154,7 +155,6 @@ subroutine canopy_structure( currentSite , bc_in ) ! ! !USES: - use EDParamsMod, only : comp_excln_exp use EDTypesMod , only : min_patch_area ! @@ -314,7 +314,7 @@ subroutine canopy_structure( currentSite , bc_in ) if(patch_area_counter > max_patch_iterations .and. area_not_balanced) then write(fates_log(),*) 'PATCH AREA CHECK NOT CLOSING' write(fates_log(),*) 'patch area:',currentpatch%area - write(fates_lot(),*) 'fraction that is imperfect (unclosed):',imperfect_fraction + write(fates_log(),*) 'fraction that is imperfect (unclosed):',imperfect_fraction do i_lyr = 1,z write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) write(fates_log(),*) 'rel error: ',(arealayer(i_lyr)- & @@ -422,7 +422,9 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) type(fates_cohort_type), pointer :: cohort type(fates_cohort_type), pointer :: copyc - real(r8) :: sumpd_carea ! Sum crown area of all cohorts in layer [m2/ha] + real(r8) :: promdem_area ! Actual area promoted or demoted (minimum of target + ! and existing canopy area) + real(r8) :: sumpd_area ! Sum crown area of all cohorts in layer [m2/ha] real(r8) :: group_area ! Sum area of cohorts with the same height [m2/ha] real(r8) :: remainder_area ! The area that has not been accounted real(r8) :: excess_area ! The area that could not be accounted @@ -453,7 +455,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! note that this is inconsequential for probabalistic ic = 0 - layer_area = 0._r8 + group_area = 0._r8 if(phase==demotion_phase) then cohort => patch%shortest ilyr_change = -1 @@ -466,7 +468,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ic = ic + 1 call carea_allom(cohort%dbh,cohort%n,site%spread, & cohort%pft,cohort%crowndamage,cohort%c_area) - layer_area = layer_area + cohort%c_area + group_area = group_area + cohort%c_area layer_co(ic)%p => cohort end if if(phase==demotion_phase) then @@ -478,7 +480,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! We update the target area to be no more than the ! area of the layer (can't take more than there is..) - target_area = min(target_area,layer_area) + promdem_area = min(target_area,group_area) ! Store the number of cohorts in the layer @@ -517,7 +519,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) remainder_area = 0._r8 do ic = 1,n_layer cohort => layer_co(ic)%p - attempt_area = target_area*layer_co(ic)%pd_area/sumpd_area + attempt_area = promdem_area*layer_co(ic)%pd_area/sumpd_area if(attempt_area>cohort%c_area)then excess_area = excess_area + (attempt_area - cohort%c_area) else @@ -533,7 +535,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! remove from them the same fraction of their remaining space if (abs(layer_co(ic)%pd_area-cohort%c_area) > nearzero) then layer_co(ic)%pd_area = layer_co(ic)%pd_area + & - (excess_area/remainder) * & + (excess_area/remainder_area) * & (cohort%c_area - layer_co(ic)%pd_area) end if end do @@ -548,7 +550,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) sumpd_area = 0._r8 ic = 1 - do while( ic<=n_layer .and. (target_area-sumpd_area)>co_area_target_precision) + do while( ic<=n_layer .and. (promdem_area-sumpd_area)>co_area_target_precision) cohort => layer_co(ic)%p @@ -566,9 +568,9 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) end if end do check_next - remainder_area = min(target_area-sumpd_area,group_area) + remainder_area = min(promdem_area-sumpd_area,group_area) do ic_nn = ic,ic_n - layer_co(ic_nn)%pd_area = remainder_area*layer_co(ic_nn)%p%c_area/norm_area + layer_co(ic_nn)%pd_area = remainder_area*layer_co(ic_nn)%p%c_area/group_area sumpd_area = sumpd_area + layer_co(ic_nn)%pd_area end do @@ -606,7 +608,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! If the dem/prom area is less than zero or larger than ! the cohort area within precision checks then FAIL - whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < + whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < & co_area_target_precision ) then ! Whole cohort promotion/demotion @@ -639,7 +641,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) call InitPRTObject(copyc%prt) if( hlm_use_planthydro.eq.itrue ) then - call InitHydrCohort(currentSite,copyc) + call InitHydrCohort(site,copyc) endif call cohort%Copy(copyc) @@ -663,7 +665,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) !----------- Insert copy into linked list ------------------------! ! Since we are not changing the heights, no sorting necessary !-----------------------------------------------------------------! - copyc%shorter => ccohort + copyc%shorter => cohort if(associated(cohort%taller))then copyc%taller => cohort%taller cohort%taller%shorter => copyc diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 86d3bbeddb..8f1cdf79a7 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -55,7 +55,7 @@ module FatesHistoryInterfaceMod use FatesInterfaceTypesMod , only : hlm_freq_day use FatesInterfaceTypesMod , only : hlm_parteh_mode use FatesInterfaceTypesMod , only : hlm_use_sp - use EDParamsMod , only : ED_val_comp_excln + use EDParamsMod , only : comp_excln_exp use EDParamsMod , only : ED_val_phen_coldtemp use EDParamsMod , only : nlevleaf use EDParamsMod , only : ED_val_history_height_bin_edges @@ -2657,7 +2657,7 @@ subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) hio_ncl_si(io_si) = hio_ncl_si(io_si) + cpatch%ncl_p * cpatch%area * AREA_INV ! only valid when "strict ppa" enabled - if ( ED_val_comp_excln .lt. 0._r8 ) then + if ( comp_excln_exp .lt. 0._r8 ) then hio_zstar_si(io_si) = hio_zstar_si(io_si) & + cpatch%zstar * cpatch%area * AREA_INV end if @@ -4780,7 +4780,7 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) end do ! only valid when "strict ppa" enabled - if ( ED_val_comp_excln .lt. 0._r8 ) then + if ( comp_excln_exp .lt. 0._r8 ) then hio_zstar_si_age(io_si,cpatch%age_class) = hio_zstar_si_age(io_si,cpatch%age_class) & + cpatch%zstar * patch_area_div_site_area end if @@ -7177,7 +7177,7 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_npatches_si_age) - if ( ED_val_comp_excln .lt. 0._r8 ) then ! only valid when "strict ppa" enabled + if ( comp_excln_exp .lt. 0._r8 ) then ! only valid when "strict ppa" enabled tempstring = 'active' else tempstring = 'inactive' From cbca2688a2eeb12b24d57d0998b313f0a5f06b8a Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 16 Jun 2025 09:00:08 -0600 Subject: [PATCH 102/194] removed termination during promotion/demotion --- biogeochem/EDCanopyStructureMod.F90 | 72 +++++++++++------------------ 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 0484fc9f6b..a6a67068d4 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -201,8 +201,13 @@ subroutine canopy_structure( currentSite , bc_in ) do while (associated(currentPatch)) ! Patch loop ! Make sure we are sorted - call currentPatch%SortCohorts(check_order=.true.) + ! call currentPatch%SortCohorts(check_order=.true.) + ! Terminate cohorts before organizing canopy. That + ! step will be interested in preserving area, so termination + ! during that step will be counter productive + call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) + call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) ! ------------------------------------------------------------------------------ ! Perform numerical checks on some cohort and patch structures @@ -232,10 +237,6 @@ subroutine canopy_structure( currentSite , bc_in ) ! the layers below. ! --------------------------------------------------------------------------- - ! Its possible that before we even enter this scheme - ! some cohort numbers are very low. Terminate them. - call terminate_cohorts(currentSite, currentPatch, 1, 12, bc_in) - ! Calculate how many layers we have in this canopy ! This also checks the understory to see if its crown ! area is large enough to warrant a temporary sub-understory layer @@ -247,15 +248,8 @@ subroutine canopy_structure( currentSite , bc_in ) call PromoteOrDemote(currentSite, currentPatch, i_lyr, demotion_phase, target_area) end do - ! After demotions, we may then again have cohorts that - ! are very very very sparse, remove them - call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) - call fuse_cohorts(currentSite, currentPatch, bc_in) - ! Remove cohorts for various other reasons - call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) - ! --------------------------------------------------------------------------------------- ! Promotion Phase: Identify if any upper-layers are underful and layers below them ! have cohorts that can be split and promoted to the layer above. @@ -272,14 +266,8 @@ subroutine canopy_structure( currentSite , bc_in ) call PromoteOrDemote(currentSite, currentPatch, i_lyr, promotion_phase, target_area) end do - ! Remove cohorts that are incredibly sparse - call terminate_cohorts(currentSite, currentPatch, 1,14,bc_in) - call fuse_cohorts(currentSite, currentPatch, bc_in) - ! Remove cohorts for various other reasons - call terminate_cohorts(currentSite, currentPatch, 2,14,bc_in) - end if ! --------------------------------------------------------------------------------------- @@ -315,33 +303,27 @@ subroutine canopy_structure( currentSite , bc_in ) write(fates_log(),*) 'PATCH AREA CHECK NOT CLOSING' write(fates_log(),*) 'patch area:',currentpatch%area write(fates_log(),*) 'fraction that is imperfect (unclosed):',imperfect_fraction - do i_lyr = 1,z - write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) - write(fates_log(),*) 'rel error: ',(arealayer(i_lyr)- & - (1._r8-imperfect_fraction)*currentPatch%area)/ & - ((1._r8-imperfect_fraction)*currentPatch%area) - write(fates_log(),*) 'abs error: ',arealayer(i_lyr) - & - (1._r8-imperfect_fraction)*currentPatch%area - enddo write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon write(fates_log(),*) 'spread:',currentSite%spread - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - write(fates_log(),*) 'coh ilayer:',currentCohort%canopy_layer - write(fates_log(),*) 'coh dbh:',currentCohort%dbh - write(fates_log(),*) 'coh pft:',currentCohort%pft - write(fates_log(),*) 'coh n:',currentCohort%n - write(fates_log(),*) 'coh carea:',currentCohort%c_area - ipft=currentCohort%pft - write(fates_log(),*) 'maxh:',prt_params%allom_dbh_maxheight(ipft) - write(fates_log(),*) 'lmode: ',prt_params%allom_lmode(ipft) - write(fates_log(),*) 'd2bl2: ',prt_params%allom_d2bl2(ipft) - write(fates_log(),*) 'd2bl_ediff: ',prt_params%allom_blca_expnt_diff(ipft) - write(fates_log(),*) 'd2ca_min: ',prt_params%allom_d2ca_coefficient_min(ipft) - write(fates_log(),*) 'd2ca_max: ',prt_params%allom_d2ca_coefficient_max(ipft) - currentCohort => currentCohort%shorter + do i_lyr = 1,z + write(fates_log(),*) '-----------------------------------------' + write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) + write(fates_log(),*) 'abs error (layer-patch): ',(arealayer(i_lyr)- & + (1._r8-imperfect_fraction)*currentPatch%area) + currentCohort => currentPatch%tallest + do while (associated(currentCohort)) + if(currentCohort%canopy_layer == i_lyr)then + write(fates_log(),*) '-----------' + write(fates_log(),*) ' co area:',currentCohort%c_area + write(fates_log(),*) ' co dbh: ',currentCohort%dbh + write(fates_log(),*) ' co pft: ',currentCohort%pft + write(fates_log(),*) ' co n: ',currentCohort%n + end if + currentCohort => currentCohort%shorter + end do enddo + call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -445,8 +427,6 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! to help track which cohorts are in the target layer associate(layer_co => patch%co_scr) - - ! Step 1: Determine which cohorts are in the layer ! and point to them in the scratch vector ! Make sure their areas are updated too. @@ -458,10 +438,10 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) group_area = 0._r8 if(phase==demotion_phase) then cohort => patch%shortest - ilyr_change = -1 + ilyr_change = 1 else cohort => patch%tallest - ilyr_change = 1 + ilyr_change = -1 end if do while (associated(cohort)) if(cohort%canopy_layer == target_layer)then @@ -582,7 +562,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! Check to make sure the changes are within bounds do ic = 1,n_layer cohort => layer_co(ic)%p - if( (layer_co(ic)%pd_area > cohort%c_area) .or. & + if( ((layer_co(ic)%pd_area - cohort%c_area) > co_area_target_precision ) .or. & (layer_co(ic)%pd_area < 0._r8) ) then write(fates_log(),*) 'negative,or more area than the cohort has is being promoted/demoted' write(fates_log(),*) 'change: ',layer_co(ic)%pd_area From 7dbfccaee89b7784270c7f6f43f639f553a8aa38 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 16 Jun 2025 12:40:03 -0600 Subject: [PATCH 103/194] Rename is_wild_fire() to fire_has_ignitions_and_intensity(). --- fire/FatesRxFireMod.F90 | 4 ++-- fire/SFMainMod.F90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 index eecb771344..545aea4fd3 100644 --- a/fire/FatesRxFireMod.F90 +++ b/fire/FatesRxFireMod.F90 @@ -49,7 +49,7 @@ end logical function is_prescribed_burn !--------------------------------------------------------------------------------------- - logical function is_wild_fire(wildfire_FI, wildfire_ignitions, rxfire_maxFI, & + logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignitions, rxfire_maxFI, & wildfire_intensity_thresh) ! ! DESCRIPTION: @@ -79,6 +79,6 @@ logical function is_wild_fire(wildfire_FI, wildfire_ignitions, rxfire_maxFI, is_wildfire = managed_wildfire .or. true_wildfire - end logical function is_wild_fire + end logical function fire_has_ignitions_and_intensity end module FatesRxFireMod \ No newline at end of file diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 126768662b..93dc4a0632 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -371,7 +371,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) use SFParamsMod, only : SF_val_rxfire_maxthreshold, SF_val_rxfire_fuel_min use SFParamsMod, only : SF_val_rxfire_fuel_max use EDParamsMod, only : rxfire_switch - use FatesRxFireMod, only : is_prescribed_burn, is_wild_fire + use FatesRxFireMod, only : is_prescribed_burn, fire_has_ignitions_and_intensity ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -423,7 +423,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) is_rxfire = is_prescribed_burn(currentPatch%FI, currentSite%NF, & SF_val_rxfire_minthreshold, SF_val_rxfire_maxthreshold, SF_val_fire_threshold) - is_wildfire = is_wild_fire(currentPatch%FI, currentSite%NF, SF_val_rxfire_minthreshold, & + is_wildfire = fire_has_ignitions_and_intensity(currentPatch%FI, currentSite%NF, SF_val_rxfire_minthreshold, & SF_val_fire_threshold) if (is_rxfire) then From 6d23025e7d59de76b0f5d8f3ecc4700ad48b73c1 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 16 Jun 2025 12:42:05 -0600 Subject: [PATCH 104/194] fire_has_ignitions_and_intensity(): Remove unused rx/not distinction. --- fire/FatesRxFireMod.F90 | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 index 545aea4fd3..451146c7aa 100644 --- a/fire/FatesRxFireMod.F90 +++ b/fire/FatesRxFireMod.F90 @@ -49,7 +49,7 @@ end logical function is_prescribed_burn !--------------------------------------------------------------------------------------- - logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignitions, rxfire_maxFI, & + logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignitions, & wildfire_intensity_thresh) ! ! DESCRIPTION: @@ -59,25 +59,16 @@ logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignition ! ARGUMENTS: real(r8), intent(in) :: wildfire_FI ! wildfire fire intensity [kW/m] real(r8), intent(in) :: wildfire_ignitions ! wildfire ignitions [count/km2/day] - real(r8), intent(in) :: rx_max_FI ! maximum fire energy of prescribed fire [kW/m] real(r8), intent(in) :: wildfire_FI_thresh ! threshold for fires that spread or go out [kW/m] ! LOCALS: - logical :: managed_wildfire ! is it a wildfire with FI lower than the max rxfire intensity? [can either be Rx fire or wildfire] - logical :: true_wildfire ! is it a wildfire that cannot be managed? logical :: has_ignitions ! any natural ignitions at the site? logical :: above_wildfire_thresh ! above the wildfire energy threshold has_ignitions = wildfire_ignitions > nearzero above_wildfire_thresh = wildfire_FI > wildfire_FI_thresh - managed_wildfire = has_ignitions .and. above_wildfire_thresh .and. & - wildfire_FI < rx_max_FI - - true_wildfire = has_ignitions .and. above_wildfire_thresh .and. & - wildfire_FI > rx_max_FI - - is_wildfire = managed_wildfire .or. true_wildfire + is_wildfire = has_ignitions .and. above_wildfire_thresh end logical function fire_has_ignitions_and_intensity From e8de4c721238b4cd5df770f63541cea98b4f01da Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 16 Jun 2025 12:43:09 -0600 Subject: [PATCH 105/194] fire_has_ignitions_and_intensity(): Compile fix. --- fire/FatesRxFireMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 index 451146c7aa..50b23319da 100644 --- a/fire/FatesRxFireMod.F90 +++ b/fire/FatesRxFireMod.F90 @@ -50,7 +50,7 @@ end logical function is_prescribed_burn !--------------------------------------------------------------------------------------- logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignitions, & - wildfire_intensity_thresh) + wildfire_FI_thresh) ! ! DESCRIPTION: ! Determines if a wildfire is happening From 8a23189509e6d71cee5e7f3126302de4b027bbce Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Mon, 16 Jun 2025 13:09:53 -0600 Subject: [PATCH 106/194] Delete fire_has_ignitions_and_intensity(). --- fire/FatesRxFireMod.F90 | 25 ------------------------- fire/SFMainMod.F90 | 17 ++++++----------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 index 50b23319da..17c2084237 100644 --- a/fire/FatesRxFireMod.F90 +++ b/fire/FatesRxFireMod.F90 @@ -47,29 +47,4 @@ logical function is_prescribed_burn(wildfire_FI, wildfire_ignitions, rx_min_FI, end logical function is_prescribed_burn - !--------------------------------------------------------------------------------------- - - logical function fire_has_ignitions_and_intensity(wildfire_FI, wildfire_ignitions, & - wildfire_FI_thresh) - ! - ! DESCRIPTION: - ! Determines if a wildfire is happening - ! - - ! ARGUMENTS: - real(r8), intent(in) :: wildfire_FI ! wildfire fire intensity [kW/m] - real(r8), intent(in) :: wildfire_ignitions ! wildfire ignitions [count/km2/day] - real(r8), intent(in) :: wildfire_FI_thresh ! threshold for fires that spread or go out [kW/m] - - ! LOCALS: - logical :: has_ignitions ! any natural ignitions at the site? - logical :: above_wildfire_thresh ! above the wildfire energy threshold - - has_ignitions = wildfire_ignitions > nearzero - above_wildfire_thresh = wildfire_FI > wildfire_FI_thresh - - is_wildfire = has_ignitions .and. above_wildfire_thresh - - end logical function fire_has_ignitions_and_intensity - end module FatesRxFireMod \ No newline at end of file diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 93dc4a0632..c511348d5f 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -371,7 +371,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) use SFParamsMod, only : SF_val_rxfire_maxthreshold, SF_val_rxfire_fuel_min use SFParamsMod, only : SF_val_rxfire_fuel_max use EDParamsMod, only : rxfire_switch - use FatesRxFireMod, only : is_prescribed_burn, fire_has_ignitions_and_intensity + use FatesRxFireMod, only : is_prescribed_burn ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -380,8 +380,8 @@ subroutine CalculateSurfaceFireIntensity(currentSite) type(fates_patch_type), pointer :: currentPatch ! patch object real(r8) :: fuel_consumed(num_fuel_classes) ! fuel consumed [kgC/m2] logical :: is_rxfire ! is it a prescribed fire? - logical :: is_wildfire ! combine both managed and true wildfire for now logical :: rxfire_fuel_check ! is fuel within thresholds for prescribed burn + logical :: fi_check ! is (potential) fire intensity high enough for fire to actually happen? currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -408,6 +408,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! fire intensity [kW/m] currentPatch%FI = FireIntensity(currentPatch%TFC_ROS/0.45_r8, currentPatch%ROS_front/60.0_r8) + fi_check = currentPatch%FI > SF_val_fire_threshold ! check if prescribed fire can occur based on fuel load rxfire_fuel_check = currentPatch%fuel%non_trunk_loading > SF_val_rxfire_fuel_min .and. & @@ -423,22 +424,16 @@ subroutine CalculateSurfaceFireIntensity(currentSite) is_rxfire = is_prescribed_burn(currentPatch%FI, currentSite%NF, & SF_val_rxfire_minthreshold, SF_val_rxfire_maxthreshold, SF_val_fire_threshold) - is_wildfire = fire_has_ignitions_and_intensity(currentPatch%FI, currentSite%NF, SF_val_rxfire_minthreshold, & - SF_val_fire_threshold) - if (is_rxfire) then currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check currentPatch%rx_fire = 1 - else if (is_wildfire) then + else if (fi_check) then ! (potential) intensity is greater than kW/m energy threshold currentPatch%nonrx_fire = 1 end if - else ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on - ! track wildfires greater than kW/m energy threshold - if (currentPatch%FI > SF_val_fire_threshold) then - currentPatch%nonrx_fire = 1 - end if + else if (fi_check) ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on, but (potential) intensity is greater than kW/m energy threshold + currentPatch%nonrx_fire = 1 end if ! assign fire intensities and ignitions based on fire type From 2e6521f46588eac15f886dee3efd427e313aca7b Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Mon, 16 Jun 2025 20:01:34 -0700 Subject: [PATCH 107/194] add ignition check for wildfire occurence --- fire/SFMainMod.F90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index c511348d5f..715fed5bf1 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -382,6 +382,7 @@ subroutine CalculateSurfaceFireIntensity(currentSite) logical :: is_rxfire ! is it a prescribed fire? logical :: rxfire_fuel_check ! is fuel within thresholds for prescribed burn logical :: fi_check ! is (potential) fire intensity high enough for fire to actually happen? + logical :: has_ignition ! is ignition greater than zero? currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -403,8 +404,10 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch%rx_fire = 0 ! only rx fire currentPatch%rx_FI = 0.0_r8 currentPatch%nonrx_FI = 0.0_r8 + + has_ignition = currentSite%NF > 0.0_r8 - if (currentSite%NF > 0.0_r8 .or. currentSite%fireWeather%rx_flag == itrue) then + if (has_ignition .or. currentSite%fireWeather%rx_flag == itrue) then ! fire intensity [kW/m] currentPatch%FI = FireIntensity(currentPatch%TFC_ROS/0.45_r8, currentPatch%ROS_front/60.0_r8) @@ -428,11 +431,11 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check currentPatch%rx_fire = 1 - else if (fi_check) then ! (potential) intensity is greater than kW/m energy threshold + else if (has_ignition .and. fi_check) then ! (potential) intensity is greater than kW/m energy threshold currentPatch%nonrx_fire = 1 end if - else if (fi_check) ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on, but (potential) intensity is greater than kW/m energy threshold + else if (has_ignition .and. fi_check) ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on, but (potential) intensity is greater than kW/m energy threshold currentPatch%nonrx_fire = 1 end if From ea26256a743336ec0c4d0803453aed6c49c204b1 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Wed, 18 Jun 2025 09:44:44 -0700 Subject: [PATCH 108/194] fixing duplicates that came up with main merge --- testing/testing_shr/FatesFactoryMod.F90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index ed1785a49c..81c0288a36 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -54,7 +54,7 @@ module FatesFactoryMod use FatesAllometryMod, only : bdead_allom use FatesAllometryMod, only : bstore_allom use FatesAllometryMod, only : carea_allom - use FatesInterfaceTypesMod, only : hlm_parteh_mode, hlm_regeneration_model + use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : nleafage use FatesSizeAgeTypeIndicesMod, only : get_age_class_index use FatesInterfaceTypesMod, only : hlm_regeneration_model @@ -112,8 +112,6 @@ subroutine InitializeGlobals(step_size) do i = 2,nlevleaf dlower_vai(i) = dlower_vai(i-1) + dinc_vai(i-1) end do - - hlm_regeneration_model = default_regeneration end subroutine InitializeGlobals From 4c334ac44f731d6922d10dd3fbc1339c077bf5ad Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Fri, 20 Jun 2025 11:56:28 -0600 Subject: [PATCH 109/194] Move functional test plotting functions to new module. Allows unit tests to be able to run without requiring matplotlib. --- .../allometry/allometry_test.py | 3 +- .../functional_testing/fire/ros/ros_test.py | 2 +- .../math_utils/math_utils_test.py | 2 +- .../functional_testing/patch/patch_test.py | 3 +- testing/utils.py | 100 +---------------- testing/utils_plotting.py | 101 ++++++++++++++++++ 6 files changed, 109 insertions(+), 102 deletions(-) create mode 100644 testing/utils_plotting.py diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index d75fd8f279..90e048a4ae 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -6,7 +6,8 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from utils import round_up, get_color_palette, blank_plot +from utils import round_up +from utils_plotting import blank_plot, get_color_palette from functional_class import FunctionalTest diff --git a/testing/functional_testing/fire/ros/ros_test.py b/testing/functional_testing/fire/ros/ros_test.py index 8bdec046d8..d7248c72f5 100644 --- a/testing/functional_testing/fire/ros/ros_test.py +++ b/testing/functional_testing/fire/ros/ros_test.py @@ -7,7 +7,7 @@ import pandas as pd import matplotlib.pyplot as plt from functional_class import FunctionalTest -from utils import blank_plot +from utils_plotting import blank_plot COLORS = ["#793922", "#6B8939", "#99291F", "#CC9728", "#2C778A"] CM_TO_FT = 30.48 diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py index b9c82fae83..db9b5aaef3 100644 --- a/testing/functional_testing/math_utils/math_utils_test.py +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -5,7 +5,7 @@ import xarray as xr import numpy as np import matplotlib.pyplot as plt -from utils import get_color_palette +from utils_plotting import get_color_palette from functional_class import FunctionalTest diff --git a/testing/functional_testing/patch/patch_test.py b/testing/functional_testing/patch/patch_test.py index c0201b6a2b..09ac477cf6 100644 --- a/testing/functional_testing/patch/patch_test.py +++ b/testing/functional_testing/patch/patch_test.py @@ -6,7 +6,8 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from utils import round_up, get_color_palette, blank_plot +from utils import round_up +from utils_plotting import blank_plot, get_color_palette from functional_class import FunctionalTest diff --git a/testing/utils.py b/testing/utils.py index 57b49d79fe..320142ccbe 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -1,11 +1,11 @@ -"""Utility functions for plotting, file checking, math equations, etc. +"""Utility functions for file checking, math equations, etc. +Do not include any third-party modules here. """ import math import os import configparser import argparse -import matplotlib.pyplot as plt from path_utils import add_cime_lib_to_path add_cime_lib_to_path() @@ -76,53 +76,6 @@ def copy_file(file_path: str, directory) -> str: return file_basename -def get_color_palette(number: int) -> list: - """_summary_ - - Args: - number (int): number of colors to get - must be <= 20 - - Raises: - ValueError: number must be less than hard-coded list - - Returns: - list[tuple]: list of colors to use in plotting - """ - - # hard-coded list of colors, can add more here if necessary - all_colors = [ - (31, 119, 180), - (174, 199, 232), - (255, 127, 14), - (255, 187, 120), - (44, 160, 44), - (152, 223, 138), - (214, 39, 40), - (255, 152, 150), - (148, 103, 189), - (197, 176, 213), - (140, 86, 75), - (196, 156, 148), - (227, 119, 194), - (247, 182, 210), - (127, 127, 127), - (199, 199, 199), - (188, 189, 34), - (219, 219, 141), - (23, 190, 207), - (158, 218, 229), - ] - - if number > len(all_colors): - raise ValueError(f"get_color_palette: number must be <= {len(all_colors)}") - - colors = [ - (red / 255.0, green / 255.0, blue / 255.0) for red, green, blue in all_colors - ] - - return colors[:number] - - def get_abspath_from_config_file(relative_path, config_file): """ Gets the absolute path of a file relative to the config file where it was defined. @@ -246,52 +199,3 @@ def str_to_list(val: str) -> list: return [] res = val.strip("][").split(",") return [n.strip() for n in res] - - -def blank_plot( - x_max: float, - x_min: float, - y_max: float, - y_min: float, - draw_horizontal_lines: bool = False, -): - """Generate a blank plot with set attributes - - Args: - x_max (float): maximum x value - x_min (float): minimum x value - y_max (float): maximum y value - y_min (float): minimum y value - draw_horizontal_lines (bool, optional): whether or not to draw horizontal - lines across plot. Defaults to False. - """ - - plt.figure(figsize=(7, 5)) - axis = plt.subplot(111) - axis.spines["top"].set_visible(False) - axis.spines["bottom"].set_visible(False) - axis.spines["right"].set_visible(False) - axis.spines["left"].set_visible(False) - - axis.get_xaxis().tick_bottom() - axis.get_yaxis().tick_left() - - plt.xlim(0.0, x_max) - plt.ylim(0.0, y_max) - - plt.yticks(fontsize=10) - plt.xticks(fontsize=10) - - if draw_horizontal_lines: - inc = (int(y_max) - y_min) / 20 - for i in range(0, 20): - plt.plot( - range(math.floor(x_min), math.ceil(x_max)), - [0.0 + i * inc] * len(range(math.floor(x_min), math.ceil(x_max))), - "--", - lw=0.5, - color="black", - alpha=0.3, - ) - - plt.tick_params(bottom=False, top=False, left=False, right=False) diff --git a/testing/utils_plotting.py b/testing/utils_plotting.py new file mode 100644 index 0000000000..df38b80617 --- /dev/null +++ b/testing/utils_plotting.py @@ -0,0 +1,101 @@ +"""Utility functions for plotting +""" + +import math +import matplotlib.pyplot as plt + + +def blank_plot( + x_max: float, + x_min: float, + y_max: float, + y_min: float, + draw_horizontal_lines: bool = False, +): + """Generate a blank plot with set attributes + + Args: + x_max (float): maximum x value + x_min (float): minimum x value + y_max (float): maximum y value + y_min (float): minimum y value + draw_horizontal_lines (bool, optional): whether or not to draw horizontal + lines across plot. Defaults to False. + """ + + plt.figure(figsize=(7, 5)) + axis = plt.subplot(111) + axis.spines["top"].set_visible(False) + axis.spines["bottom"].set_visible(False) + axis.spines["right"].set_visible(False) + axis.spines["left"].set_visible(False) + + axis.get_xaxis().tick_bottom() + axis.get_yaxis().tick_left() + + plt.xlim(0.0, x_max) + plt.ylim(0.0, y_max) + + plt.yticks(fontsize=10) + plt.xticks(fontsize=10) + + if draw_horizontal_lines: + inc = (int(y_max) - y_min) / 20 + for i in range(0, 20): + plt.plot( + range(math.floor(x_min), math.ceil(x_max)), + [0.0 + i * inc] * len(range(math.floor(x_min), math.ceil(x_max))), + "--", + lw=0.5, + color="black", + alpha=0.3, + ) + + plt.tick_params(bottom=False, top=False, left=False, right=False) + + +def get_color_palette(number: int) -> list: + """_summary_ + + Args: + number (int): number of colors to get - must be <= 20 + + Raises: + ValueError: number must be less than hard-coded list + + Returns: + list[tuple]: list of colors to use in plotting + """ + + # hard-coded list of colors, can add more here if necessary + all_colors = [ + (31, 119, 180), + (174, 199, 232), + (255, 127, 14), + (255, 187, 120), + (44, 160, 44), + (152, 223, 138), + (214, 39, 40), + (255, 152, 150), + (148, 103, 189), + (197, 176, 213), + (140, 86, 75), + (196, 156, 148), + (227, 119, 194), + (247, 182, 210), + (127, 127, 127), + (199, 199, 199), + (188, 189, 34), + (219, 219, 141), + (23, 190, 207), + (158, 218, 229), + ] + + if number > len(all_colors): + raise ValueError(f"get_color_palette: number must be <= {len(all_colors)}") + + colors = [ + (red / 255.0, green / 255.0, blue / 255.0) for red, green, blue in all_colors + ] + + return colors[:number] From c65df334c62ce6ffebd2166d45d090621c3df857 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 21 Jun 2025 09:33:55 -0600 Subject: [PATCH 110/194] Add FunctionalTestWithDrivers to handle fuel test. --- testing/functional_class.py | 15 +------- testing/functional_class_with_drivers.py | 32 +++++++++++++++++ .../allometry/allometry_test.py | 2 -- .../functional_testing/fire/fuel/fuel_test.py | 8 ++--- .../functional_testing/fire/ros/ros_test.py | 2 -- .../math_utils/math_utils_test.py | 2 -- .../functional_testing/patch/patch_test.py | 2 -- testing/functional_tests.cfg | 8 ----- testing/load_functional_tests.py | 1 + testing/run_functional_tests.py | 34 ++++++++++++++----- 10 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 testing/functional_class_with_drivers.py diff --git a/testing/functional_class.py b/testing/functional_class.py index 50da8e6c5b..5f5f4bbabf 100644 --- a/testing/functional_class.py +++ b/testing/functional_class.py @@ -7,28 +7,15 @@ class FunctionalTest(ABC): """Class for running FATES functional tests""" def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, - use_param_file:str, datm_file:str, datm_file_url:str, other_args:str): + use_param_file:str, other_args:str): self.name = name self.test_dir = test_dir self.test_exe = test_exe self.out_file = out_file self.use_param_file = str_to_bool(use_param_file) - self.datm_file = None self.other_args = str_to_list(other_args) self.plot = False - # Check that datm exists and save its absolute path - if datm_file: - self.datm_file = os.path.abspath(datm_file) - if not os.path.exists(self.datm_file): - if not datm_file_url: - raise FileNotFoundError(f"datm_file not found: '{self.datm_file}'") - datm_file_dir = os.path.dirname(self.datm_file) - if not os.path.isdir(datm_file_dir): - os.makedirs(datm_file_dir) - print(f"Downloading datm_file from {datm_file_url}") - urllib.request.urlretrieve(datm_file_url, self.datm_file) - @abstractmethod def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str): pass diff --git a/testing/functional_class_with_drivers.py b/testing/functional_class_with_drivers.py new file mode 100644 index 0000000000..6da3bb4930 --- /dev/null +++ b/testing/functional_class_with_drivers.py @@ -0,0 +1,32 @@ +import os +import urllib.request +from functional_class import FunctionalTest + + +class FunctionalTestWithDrivers(FunctionalTest): + """Class for running FATES functional tests with driver files""" + + def __init__(self, datm_file: str, datm_file_url: str, *args): + + # Things that are set up in super().__init__() + self.name = None + self.test_dir = None + self.test_exe = None + self.out_file = None + self.use_param_file = None + self.datm_file = None + self.other_args = None + self.plot = False + + # Check that datm exists and save its absolute path + self.datm_file = os.path.abspath(datm_file) + if not os.path.exists(self.datm_file): + if not datm_file_url: + raise FileNotFoundError(f"datm_file not found: '{self.datm_file}'") + datm_file_dir = os.path.dirname(self.datm_file) + if not os.path.isdir(datm_file_dir): + os.makedirs(datm_file_dir) + print(f"Downloading datm_file from {datm_file_url}") + urllib.request.urlretrieve(datm_file_url, self.datm_file) + + super().__init__(*args) diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index 90e048a4ae..4b070bc8db 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -24,8 +24,6 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], - test_dict["datm_file"], - test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/fuel/fuel_test.py b/testing/functional_testing/fire/fuel/fuel_test.py index 9a6ec034bc..c2331094ef 100644 --- a/testing/functional_testing/fire/fuel/fuel_test.py +++ b/testing/functional_testing/fire/fuel/fuel_test.py @@ -5,23 +5,23 @@ import numpy as np import xarray as xr import matplotlib.pyplot as plt -from functional_class import FunctionalTest +from functional_class_with_drivers import FunctionalTestWithDrivers -class FuelTest(FunctionalTest): +class FuelTest(FunctionalTestWithDrivers): """Fuel test class""" name = "fuel" def __init__(self, test_dict): super().__init__( + test_dict["datm_file"], + test_dict["datm_file_url"], FuelTest.name, test_dict["test_dir"], test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], - test_dict["datm_file"], - test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/fire/ros/ros_test.py b/testing/functional_testing/fire/ros/ros_test.py index d7248c72f5..56c7469095 100644 --- a/testing/functional_testing/fire/ros/ros_test.py +++ b/testing/functional_testing/fire/ros/ros_test.py @@ -26,8 +26,6 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], - test_dict["datm_file"], - test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py index db9b5aaef3..df6d7fb173 100644 --- a/testing/functional_testing/math_utils/math_utils_test.py +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -22,8 +22,6 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], - test_dict["datm_file"], - test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_testing/patch/patch_test.py b/testing/functional_testing/patch/patch_test.py index 09ac477cf6..0610fd521e 100644 --- a/testing/functional_testing/patch/patch_test.py +++ b/testing/functional_testing/patch/patch_test.py @@ -24,8 +24,6 @@ def __init__(self, test_dict): test_dict["test_exe"], test_dict["out_file"], test_dict["use_param_file"], - test_dict["datm_file"], - test_dict["datm_file_url"], test_dict["other_args"], ) self.plot = True diff --git a/testing/functional_tests.cfg b/testing/functional_tests.cfg index d5bc1a3c23..e6339485a7 100644 --- a/testing/functional_tests.cfg +++ b/testing/functional_tests.cfg @@ -3,8 +3,6 @@ test_dir = fates_allom_ftest test_exe = FATES_allom_exe out_file = allometry_out.nc use_param_file = True -datm_file = -datm_file_url = other_args = [] [quadratic] @@ -12,8 +10,6 @@ test_dir = fates_math_ftest test_exe = FATES_math_exe out_file = quad_out.nc use_param_file = False -datm_file = -datm_file_url = other_args = [] [fuel] @@ -30,8 +26,6 @@ test_dir = fates_ros_ftest test_exe = FATES_ros_exe out_file = ros_out.nc use_param_file = True -datm_file = -datm_file_url = other_args = [] [patch] @@ -39,6 +33,4 @@ test_dir = fates_patch_ftest test_exe = FATES_patch_exe out_file = None use_param_file = True -datm_file = -datm_file_url = other_args = [] diff --git a/testing/load_functional_tests.py b/testing/load_functional_tests.py index 7b2051f15d..275dc15868 100644 --- a/testing/load_functional_tests.py +++ b/testing/load_functional_tests.py @@ -1,6 +1,7 @@ # add testing subclasses here from functional_class import FunctionalTest +from functional_class_with_drivers import FunctionalTestWithDrivers from functional_testing.allometry.allometry_test import AllometryTest from functional_testing.math_utils.math_utils_test import QuadraticTest from functional_testing.fire.fuel.fuel_test import FuelTest diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 7186f76aa2..83503ae67b 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -32,6 +32,7 @@ import matplotlib.pyplot as plt from build_fortran_tests import build_tests, build_exists +from functional_class_with_drivers import FunctionalTestWithDrivers from path_utils import add_cime_lib_to_path from utils import copy_file, create_nc_from_cdl, config_to_dict, parse_test_list @@ -45,12 +46,14 @@ # constants for this script _FILE_DIR = os.path.dirname(__file__) _DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "functional_tests.cfg") -_DEFAULT_CDL_PATH = os.path.abspath(os.path.join( - _FILE_DIR, - os.pardir, - "parameter_files", - "fates_params_default.cdl", -)) +_DEFAULT_CDL_PATH = os.path.abspath( + os.path.join( + _FILE_DIR, + os.pardir, + "parameter_files", + "fates_params_default.cdl", + ) +) _CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir) _TEST_SUB_DIR = "testing" @@ -311,7 +314,7 @@ def run_functional_tests( for _, test in test_dict.items(): args = test.other_args # prepend datm file (if required) to argument list - if test.datm_file: + if isinstance(test, FunctionalTestWithDrivers) and test.datm_file: args.insert(0, test.datm_file) # prepend parameter file (if required) to argument list if test.use_param_file: @@ -411,6 +414,17 @@ def run_fortran_exectuables(build_dir, test_dir, test_exe, run_dir, args): print(out) +def get_test_subclasses(*argv): + """ + Given a FunctionalTest* class, find all its test subclasses. Do not include child + FunctionalTest* classes. + """ + test_subclasses = [] + for ftest_class in argv: + test_subclasses += [x for x in ftest_class.__subclasses__() if hasattr(x, "name")] + return test_subclasses + + def main(): """Main script Reads in command-line arguments and then runs the tests. @@ -422,7 +436,11 @@ def main(): config_dict = parse_test_list(full_test_dict, args.test_list) test_dict = {} - subclasses = FunctionalTest.__subclasses__() + + # Get all the possible test subclasses. + subclasses = get_test_subclasses(FunctionalTest, FunctionalTestWithDrivers) + + # Associate each test in the config file with the appropriate test subclass for name in config_dict.keys(): test_class = list(filter(lambda subclass: subclass.name == name, subclasses))[ 0 From 5e124f4eadeb43ad39819c86556922e230fb9c76 Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sat, 21 Jun 2025 10:09:17 -0600 Subject: [PATCH 111/194] Move config file check to check_arg_validity(). --- testing/run_functional_tests.py | 6 ++++++ testing/utils.py | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 83503ae67b..a2fa655c49 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -196,6 +196,12 @@ def check_arg_validity(args): ) check_build_dir(args.build_dir, args.test_dict) + # Check that config file exists and is a file + if not os.path.exists(args.config_file): + raise FileNotFoundError(args.config_file) + if not os.path.isfile(args.config_file): + raise RuntimeError(f"config 'file' is a directory: '{args.config_file}'") + def check_param_file(param_file): """Checks to see if param_file exists and is of the correct form (.nc or .cdl) diff --git a/testing/utils.py b/testing/utils.py index 320142ccbe..55978cc7d0 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -107,12 +107,6 @@ def config_to_dict(config_file: str) -> dict: dictionary: dictionary of config file """ - # Check that config file exists and is a file - if not os.path.exists(config_file): - raise FileNotFoundError(config_file) - if not os.path.isfile(config_file): - raise RuntimeError(f"config_file is a directory: '{config_file}'") - # Define list of config file options that we expect to be paths options_that_are_paths = ["datm_file"] From 7404a9d6426fbf7d4fb9aacf77de1c0b1c4e08bc Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 22 Jun 2025 11:39:47 -0600 Subject: [PATCH 112/194] Simplify FunctionalTestWithDrivers.__init__(). --- testing/functional_class_with_drivers.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/testing/functional_class_with_drivers.py b/testing/functional_class_with_drivers.py index 6da3bb4930..513f0a9f2c 100644 --- a/testing/functional_class_with_drivers.py +++ b/testing/functional_class_with_drivers.py @@ -8,16 +8,6 @@ class FunctionalTestWithDrivers(FunctionalTest): def __init__(self, datm_file: str, datm_file_url: str, *args): - # Things that are set up in super().__init__() - self.name = None - self.test_dir = None - self.test_exe = None - self.out_file = None - self.use_param_file = None - self.datm_file = None - self.other_args = None - self.plot = False - # Check that datm exists and save its absolute path self.datm_file = os.path.abspath(datm_file) if not os.path.exists(self.datm_file): From 620abe93ec0247bd2f4c483db82a640439c0c09c Mon Sep 17 00:00:00 2001 From: Sam Rabin Date: Sun, 22 Jun 2025 11:44:50 -0600 Subject: [PATCH 113/194] FunctionalClass: Revert unused changes. --- testing/functional_class.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testing/functional_class.py b/testing/functional_class.py index 5f5f4bbabf..6ca085ef2c 100644 --- a/testing/functional_class.py +++ b/testing/functional_class.py @@ -1,5 +1,3 @@ -import os -import urllib.request from abc import ABC, abstractmethod from utils import str_to_bool, str_to_list @@ -15,7 +13,7 @@ def __init__(self, name:str, test_dir:str, test_exe:str, out_file:str, self.use_param_file = str_to_bool(use_param_file) self.other_args = str_to_list(other_args) self.plot = False - + @abstractmethod def plot_output(self, run_dir:str, save_figs:bool, plot_dir:str): pass From cc8bd8b00b41aed8f2def83d228098becb87d143 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 24 Jun 2025 13:51:12 -0400 Subject: [PATCH 114/194] removed commented fire_closs terms --- biogeochem/EDPatchDynamicsMod.F90 | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 97008a8a50..7d10ddde50 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -1070,7 +1070,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) currentSite%mass_balance(el)%burn_flux_to_atm + & leaf_burn_frac * leaf_m * nc%n - ! This diagnostic only tracks + ! This term increments the loss flux from surviving trees currentSite%flux_diags%elem(el)%burned_liveveg = & currentSite%flux_diags%elem(el)%burned_liveveg + & leaf_burn_frac * leaf_m * nc%n * area_inv @@ -1080,12 +1080,6 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! Add burned leaf carbon to the atmospheric carbon flux ! for burning. - ! [frac/day]*[kgC/plant]*[plant/ha]*[m2/ha]*[day/s] = [kg/m2/s] - !bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + & - ! leaf_burn_frac * nc%prt%GetState(leaf_organ, carbon12_element) * & - ! nc%n * ha_per_m2 * days_per_sec - - ! Here the mass is removed from the plant if(int(prt_params%woody(currentCohort%pft)) == itrue)then call PRTBurnLosses(nc%prt, leaf_organ, leaf_burn_frac) @@ -2002,12 +1996,7 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !if(element_list(el) == carbon12_element) then - ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - !end if - ! Transfer below ground CWD (none burns) - do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%bg_cwd(c,sl) * patch_site_areadis new_litt%bg_cwd(c,sl) = new_litt%bg_cwd(c,sl) + donatable_mass*donate_m2 @@ -2035,10 +2024,6 @@ subroutine TransLitterNewPatch(currentSite, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !if(element_list(el) == carbon12_element) then - ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - !end if - ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%root_fines(dcmpy,sl) * patch_site_areadis @@ -2250,10 +2235,6 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !if(element_list(el) == carbon12_element) then - ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - !end if - call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2315,10 +2296,6 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & burned_mass = num_dead_trees * SF_val_CWD_frac_adj(c) * bstem * & currentCohort%fraction_crown_burned site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - - !if(element_list(el) == carbon12_element) then - ! bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - !end if endif new_litt%ag_cwd(c) = new_litt%ag_cwd(c) + donatable_mass * donate_m2 curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass * retain_m2 @@ -2730,7 +2707,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & end do site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2791,7 +2767,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & EDPftvarcon_inst%landusechange_frac_burned(pft) site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec + else ! all other pools can end up as timber products or burn or go to litter donatable_mass = donatable_mass * (1.0_r8-EDPftvarcon_inst%landusechange_frac_exported(pft)) * & (1.0_r8-EDPftvarcon_inst%landusechange_frac_burned(pft)) @@ -2805,8 +2781,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - !!bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec - trunk_product_site = trunk_product_site + & woodproduct_mass From bfb85341c691df61726ab6c40c1827bd336455c3 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux <7565064+glemieux@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:09:16 -0700 Subject: [PATCH 115/194] Update PULL_REQUEST_TEMPLATE with sp mode b4b check This adds a temporary check box in the pull request template to remind the integrator to check that satellite phenology mode regression tests are indeed B4B for the FATES-CLM6 code freeze. The branch rules for main have been updated to ensure that all check boxes have been clicked before merges can happen (although the FATES_admin team can bypass as necessary). --- .github/PULL_REQUEST_TEMPLATE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 87f6468203..f8424eb969 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -28,6 +28,9 @@ All checklist items must be checked to enable merging this pull request: *Integrator* - [ ] FATES PASS/FAIL regression tests were run - [ ] Evaluation of test results for answer changes was performed and results provided +- [ ] FATES-CLM6 Code Freeze: satellite phenology regression tests are b4b + +*If satellite phenology regressions are **not** b4b, please hold merge and notify the FATES development team.* ### Documentation From 47a4e80a2c2c9b6e8131e5f5d7b36579f74f2ba7 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 30 Jun 2025 10:31:02 -0700 Subject: [PATCH 116/194] update parameter file cdl and comment in patch file --- .../archive/api41.0.0_prxxx_patch_params.xml | 4 ++-- parameter_files/fates_params_default.cdl | 18 ++++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/parameter_files/archive/api41.0.0_prxxx_patch_params.xml b/parameter_files/archive/api41.0.0_prxxx_patch_params.xml index c6fc218f7b..22fd861d9e 100644 --- a/parameter_files/archive/api41.0.0_prxxx_patch_params.xml +++ b/parameter_files/archive/api41.0.0_prxxx_patch_params.xml @@ -5,7 +5,7 @@ into a single pdt-dimensioned parameter using integers to set the phenology habit --> - + + + + + + + + + + + archive/api40.0.0_060625_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + fates_leaf_theta_cj_c3 + + + fates_leaf_theta_cj_c4 + + + -999, -999, -999, -999, 13 + + + fates_rxfire_temp_upthreshold + scalar + degree C + maximum temprature threshold above which prescribed fire is disallowed + 30 + + + fates_rxfire_temp_lwthreshold + scalar + degree C + minimum temprature threshold below which prescribed fire is disallowed + 5 + + + fates_rxfire_rh_upthreshold + scalar + % + maximum relative humidity threshold above which prescribed fire is disallowed + 55 + + + fates_rxfire_rh_lwthreshold + scalar + % + minimum relative humidity threshold below which prescribed fire is disallowed + 30 + + + fates_rxfire_wind_upthreshold + scalar + % + maximum wind speed threshold above which prescribed fire is disallowed + 10 + + + fates_rxfire_wind_lwthreshold + scalar + % + minimum wind speed threshold below which prescribed fire is disallowed + 2 + + + fates_rxfire_AB + scalar + fraction/day + daily burn capacity of prescribed fire + 0.01 + + + fates_rxfire_min_threshold + scalar + kJ/m/s or kW/m + minimum energy threshold at or above which prescribed fire is disallowed + 50 + + + fates_rxfire_max_threshold + scalar + kJ/m/s or kW/m + maximum energy threshold at or above which prescribed fire is disallowed + 500 + + + fates_rxfire_fuel_min + scalar + kgC/m2 + minimum fuel load at or below which prescribed fire is disallowed + 0.5 + + + fates_rxfire_fuel_max + scalar + kgC/m2 + maximum fuel load at or above which prescribed fire is disallowed + 1.5 + + + fates_rxfire_min_frac + scalar + fraction + minimum fraction of land needs to be burnable to allow rx fire + 0.1 + + + From 71b593d0d1fb8a3ea54cf6da75542fd7b2a33e51 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Wed, 13 Aug 2025 16:58:55 -0700 Subject: [PATCH 150/194] remove old patch parameter file --- .../archive/api41.0.0_prxxx_patch_params.xml | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 parameter_files/archive/api41.0.0_prxxx_patch_params.xml diff --git a/parameter_files/archive/api41.0.0_prxxx_patch_params.xml b/parameter_files/archive/api41.0.0_prxxx_patch_params.xml deleted file mode 100644 index 22fd861d9e..0000000000 --- a/parameter_files/archive/api41.0.0_prxxx_patch_params.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - archive/api40.0.0_060625_params_default.cdl - fates_params_default.cdl - 1,2,3,4,5,6,7,8,9,10,11,12,13,14 - - - fates_leaf_theta_cj_c3 - - - fates_leaf_theta_cj_c4 - - - From f8c06397a70059713fd52b12becc1585c78c233f Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 14 Aug 2025 22:26:49 -0700 Subject: [PATCH 151/194] add dcode for scalars assuming no other non-scalar entry has come first in the patch file --- tools/UpdateParamAPI.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/UpdateParamAPI.py b/tools/UpdateParamAPI.py index b158e2961e..ba921351c8 100755 --- a/tools/UpdateParamAPI.py +++ b/tools/UpdateParamAPI.py @@ -350,6 +350,7 @@ def main(): if(dimnames[0]=='scalar' or dimnames[0]=='none' or dimnames[0]==''): dimnames = () + dcode = "d" elif(isinstance(values[0],float)): dcode = "d" else: From 2e1553d76bfacd8f5ee5bd8121efb1d0b5944103 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Thu, 14 Aug 2025 22:27:10 -0700 Subject: [PATCH 152/194] reorder the default file per UpdateParamAPI --- parameter_files/fates_params_default.cdl | 90 ++++++++++++------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 85333e18ba..e899d3ff7b 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -888,42 +888,42 @@ variables: double fates_q10_mr ; fates_q10_mr:units = "unitless" ; fates_q10_mr:long_name = "Q10 for maintenance respiration" ; - double fates_rxfire_temp_upthreshold ; - fates_rxfire_temp_upthreshold:units = "degree C"; - fates_rxfire_temp_upthreshold:long_name= "maximum temprature threshold above which prescribed fire is disallowed"; - double fates_rxfire_temp_lwthreshold ; - fates_rxfire_temp_lwthreshold:units = "degree C"; - fates_rxfire_temp_lwthreshold:long_name= "minimum temprature threshold below which prescribe fire is disallowed"; - double fates_rxfire_rh_upthreshold ; - fates_rxfire_rh_upthreshold:units = "%"; - fates_rxfire_rh_upthreshold:long_name= "maximum relative humidity threshold above which prescribed fire is disallowed"; - double fates_rxfire_rh_lwthreshold ; - fates_rxfire_rh_lwthreshold:units = "%"; - fates_rxfire_rh_lwthreshold:long_name= "minimum relative humidity threshold below which prescribed fire is disallowed"; - double fates_rxfire_wind_upthreshold ; - fates_rxfire_wind_upthreshold:units = "m/s"; - fates_rxfire_wind_upthreshold:long_name= "maximum wind speed threshold above which prescribed fire is disallowed"; - double fates_rxfire_wind_lwthreshold ; - fates_rxfire_wind_lwthreshold:units = "m/s"; - fates_rxfire_wind_lwthreshold:long_name= "minimum wind speed threshold below which prescribed fire is disallowed"; double fates_rxfire_AB ; - fates_rxfire_AB:units = "fraction/day"; - fates_rxfire_AB:long_name= "daily burn capacity of prescribed fire"; - double fates_rxfire_min_threshold ; - fates_rxfire_min_threshold:units = "kJ/m/s or kW/m"; - fates_rxfire_min_threshold:long_name= "minimum energy threshold at or above which prescribed fire is disallowed"; - double fates_rxfire_max_threshold ; - fates_rxfire_max_threshold:units = "kJ/m/s or kW/m"; - fates_rxfire_max_threshold:long_name= "maximum energy threshold at or above which prescribed fire is disallowed"; - double fates_rxfire_fuel_min ; - fates_rxfire_fuel_min:units = "kgC/m2"; - fates_rxfire_fuel_min:long_name= "minimum fuel load at or below which prescribed fire is disallowed"; + fates_rxfire_AB:units = "fraction/day" ; + fates_rxfire_AB:long_name = "daily burn capacity of prescribed fire" ; double fates_rxfire_fuel_max ; - fates_rxfire_fuel_max:units = "kgC/m2"; - fates_rxfire_fuel_max:long_name= "maximum fuel load at or above which prescribed fire is disallowed"; + fates_rxfire_fuel_max:units = "kgC/m2" ; + fates_rxfire_fuel_max:long_name = "maximum fuel load at or above which prescribed fire is disallowed" ; + double fates_rxfire_fuel_min ; + fates_rxfire_fuel_min:units = "kgC/m2" ; + fates_rxfire_fuel_min:long_name = "minimum fuel load at or below which prescribed fire is disallowed" ; + double fates_rxfire_max_threshold ; + fates_rxfire_max_threshold:units = "kJ/m/s or kW/m" ; + fates_rxfire_max_threshold:long_name = "maximum energy threshold at or above which prescribed fire is disallowed" ; double fates_rxfire_min_frac ; - fates_rxfire_min_frac:units = "fraction"; - fates_rxfire_min_frac:long_name="minimum fraction of land needs to be burnable to allow rx fire"; + fates_rxfire_min_frac:units = "fraction" ; + fates_rxfire_min_frac:long_name = "minimum fraction of land needs to be burnable to allow rx fire" ; + double fates_rxfire_min_threshold ; + fates_rxfire_min_threshold:units = "kJ/m/s or kW/m" ; + fates_rxfire_min_threshold:long_name = "minimum energy threshold at or above which prescribed fire is disallowed" ; + double fates_rxfire_rh_lwthreshold ; + fates_rxfire_rh_lwthreshold:units = "%" ; + fates_rxfire_rh_lwthreshold:long_name = "minimum relative humidity threshold below which prescribed fire is disallowed" ; + double fates_rxfire_rh_upthreshold ; + fates_rxfire_rh_upthreshold:units = "%" ; + fates_rxfire_rh_upthreshold:long_name = "maximum relative humidity threshold above which prescribed fire is disallowed" ; + double fates_rxfire_temp_lwthreshold ; + fates_rxfire_temp_lwthreshold:units = "degree C" ; + fates_rxfire_temp_lwthreshold:long_name = "minimum temprature threshold below which prescribed fire is disallowed" ; + double fates_rxfire_temp_upthreshold ; + fates_rxfire_temp_upthreshold:units = "degree C" ; + fates_rxfire_temp_upthreshold:long_name = "maximum temprature threshold above which prescribed fire is disallowed" ; + double fates_rxfire_wind_lwthreshold ; + fates_rxfire_wind_lwthreshold:units = "%" ; + fates_rxfire_wind_lwthreshold:long_name = "minimum wind speed threshold below which prescribed fire is disallowed" ; + double fates_rxfire_wind_upthreshold ; + fates_rxfire_wind_upthreshold:units = "%" ; + fates_rxfire_wind_upthreshold:long_name = "maximum wind speed threshold above which prescribed fire is disallowed" ; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; @@ -1854,29 +1854,29 @@ data: fates_q10_mr = 1.5 ; - fates_rxfire_temp_upthreshold = 30 ; + fates_rxfire_AB = 0.01 ; - fates_rxfire_temp_lwthreshold = 5 ; + fates_rxfire_fuel_max = 1.5 ; - fates_rxfire_rh_upthreshold = 55 ; + fates_rxfire_fuel_min = 0.5 ; - fates_rxfire_rh_lwthreshold = 30 ; + fates_rxfire_max_threshold = 500 ; - fates_rxfire_wind_upthreshold = 10 ; + fates_rxfire_min_frac = 0.1 ; - fates_rxfire_wind_lwthreshold = 2 ; + fates_rxfire_min_threshold = 50 ; - fates_rxfire_AB = 0.01 ; + fates_rxfire_rh_lwthreshold = 30 ; - fates_rxfire_min_threshold = 50 ; + fates_rxfire_rh_upthreshold = 55 ; - fates_rxfire_max_threshold = 500 ; + fates_rxfire_temp_lwthreshold = 5 ; - fates_rxfire_fuel_min = 0.5 ; + fates_rxfire_temp_upthreshold = 30 ; - fates_rxfire_fuel_max = 1.5 ; + fates_rxfire_wind_lwthreshold = 2 ; - fates_rxfire_min_frac = 0.1 ; + fates_rxfire_wind_upthreshold = 10 ; fates_soil_salinity = 0.4 ; From fb6eb123a9d50469631dd6c86c47db56b04e6f1b Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 15 Aug 2025 11:15:39 -0400 Subject: [PATCH 153/194] Updated text on error checking during promotion/demotion --- biogeochem/EDCanopyStructureMod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index efee3fce02..2d8de70d1c 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -617,7 +617,8 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! and not trivialy small (larger than precision ! check), then split it and move part of it ! If the dem/prom area is less than zero or larger than - ! the cohort area within precision checks then FAIL + ! the cohort area within precision checks then + ! we would have failed in the previous checks whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < & co_area_target_precision ) then @@ -626,7 +627,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) cohort%canopy_layer = cohort%canopy_layer + ilyr_change elseif( (layer_co(ic)%pd_area < cohort%c_area) .and. & - (layer_co(ic)%pd_area > co_area_target_precision ) ) then + (layer_co(ic)%pd_area > 0 ) ) then ! Partial cohort promotion/demotion From 59c46cb5d24434a45d5ee73bf15ee256aaec0b20 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 15 Aug 2025 10:28:55 -0600 Subject: [PATCH 154/194] fixed pointer to c_area in dem/prom --- biogeochem/EDCanopyStructureMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 2d8de70d1c..043cf106be 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -489,7 +489,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! it less readable)" do ic = 1,n_layer - layer_co(ic)%pd_area = layer_co(ic)%c_area + layer_co(ic)%pd_area = layer_co(ic)%p%c_area end do else From 1670ba67184d4ab331962366df0520593ec7b843 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 19 Aug 2025 09:05:53 -0700 Subject: [PATCH 155/194] merge resolution --- main/FatesHistoryInterfaceMod.F90 | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 5c6a4e7577..770f99764b 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -6911,8 +6911,13 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, index = ih_crownarea_ustory_damage_si ) end if if_crowndamage1 - + call this%set_history_var(vname='FATES_NCL', units='', & + long='number of canopy levels', & + use_default='inactive', avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_ncl_si) + if_dyn1: if(hlm_hist_level_dynam>1) then call this%set_history_var(vname='FATES_NPP_LU', units='kg m-2 s-1', & @@ -6955,7 +6960,7 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_RECRUITMENT_CFLUX_PF', units='kg m-2 yr-1', & long='total PFT-level biomass of new recruits in kg of carbon per land area', & use_default='active', avgflag='A', vtype=site_pft_r8, hlms='CLM:ALM', & - upfreq=1, ivar=ivar, initialize=initialize_variables, & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_recruitment_cflux_si_pft) call this%set_history_var(vname='FATES_LEAFC_PF', units='kg m-2', & @@ -7121,18 +7126,12 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_CANOPYAREA', units='m2 m-2', & long='canopy area per m2 land area', use_default='inactive', & - avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, & + avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index=ih_canopy_fracarea_si) - call this%set_history_var(vname='FATES_NCL', units='', & - long='number of canopy levels', & - use_default='inactive', avgflag='A', vtype=site_r8, & - hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_ncl_si) - call this%set_history_var(vname='FATES_PATCHAREA', units='m2 m-2', & long='patch area per m2 land area', use_default='inactive', & - avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, & + avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index=ih_fracarea_si) ! patch age class variables @@ -7216,14 +7215,14 @@ subroutine define_history_vars(this, initialize_variables) units='m2 m-2', & long='secondary forest patch area since anthropgenic disturbance', & use_default='inactive', avgflag='A', vtype=site_r8, & - hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_agesince_anthrodist_si) call this%set_history_var(vname='FATES_SECONDARY_AREA', & units='m2 m-2', & long='secondary forest patch area since any kind of disturbance', & use_default='inactive', avgflag='A', vtype=site_r8, & - hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_secondarylands_fracarea_si) call this%set_history_var(vname='FATES_SECONDARY_AREA_AP', & @@ -7237,7 +7236,7 @@ subroutine define_history_vars(this, initialize_variables) units='m2 m-2', & long='primary forest patch area since any kind of disturbance', & use_default='inactive', avgflag='A', vtype=site_r8, & - hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_primarylands_fracarea_si) call this%set_history_var(vname='FATES_PRIMARY_AREA_AP', & @@ -8709,8 +8708,6 @@ subroutine define_history_vars(this, initialize_variables) end if if_dyn1 end if if_dyn0 - !HERE - if_hifrq0: if(hlm_hist_level_hifrq>0) then From ba3fd1d6dff6d319f265c5b1090cc2d52817eab3 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 19 Aug 2025 12:22:58 -0400 Subject: [PATCH 156/194] fixed zstar history --- main/FatesHistoryInterfaceMod.F90 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 770f99764b..636a805304 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -6917,6 +6917,18 @@ subroutine define_history_vars(this, initialize_variables) use_default='inactive', avgflag='A', vtype=site_r8, & hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_ncl_si) + + if ( ED_val_comp_excln .lt. 0._r8 ) then ! only valid when "strict ppa" enabled + tempstring = 'active' + else + tempstring = 'inactive' + endif + + call this%set_history_var(vname='FATES_ZSTAR', units='m', & + long='product of zstar and patch area', & + use_default=tempstring, avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_zstar_si) if_dyn1: if(hlm_hist_level_dynam>1) then @@ -7180,12 +7192,6 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_zstar_si_age) - call this%set_history_var(vname='FATES_ZSTAR', units='m', & - long='product of zstar and patch area', & - use_default='inactive', avgflag='A', vtype=site_r8, & - hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_zstar_si) - call this%set_history_var(vname='FATES_CANOPYAREA_HT', units='m2 m-2', & long='canopy area height distribution', & use_default='active', avgflag='A', vtype=site_height_r8, & From fb5e6dee87935256a9472edff0e1dc0ca5626c69 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Fri, 5 Sep 2025 09:00:22 -0600 Subject: [PATCH 157/194] correct competitive exclusion parameter reference --- main/FatesHistoryInterfaceMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index cdc0861712..0ac13af5a1 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -7096,7 +7096,7 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_ncl_si) - if ( ED_val_comp_excln .lt. 0._r8 ) then ! only valid when "strict ppa" enabled + if ( comp_excln_exp .lt. 0._r8 ) then ! only valid when "strict ppa" enabled tempstring = 'active' else tempstring = 'inactive' From 0d7c6076f1e8947d8fd66f3937ac62a41a43fc95 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Wed, 10 Sep 2025 15:42:54 -0400 Subject: [PATCH 158/194] changing gpp and ar output boundary flux to use the massbalance data already available --- main/EDMainMod.F90 | 33 +++++++++------------------- main/FatesInterfaceMod.F90 | 36 ------------------------------- main/FatesInterfaceTypesMod.F90 | 29 ------------------------- main/FatesRestartInterfaceMod.F90 | 27 +++++++++++++++++++++-- 4 files changed, 35 insertions(+), 90 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 4a746c1168..81b8ea3bb7 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -426,9 +426,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) current_fates_landuse_state_vector = currentSite%get_current_landuse_statevector() - ! Clear site GPP and AR passing to HLM - bc_out%gpp_site = 0._r8 - bc_out%ar_site = 0._r8 + ! Patch level biomass are required for C-based harvest call get_harvestable_carbon(currentSite, bc_in%site_area, bc_in%hlm_harvest_catnames, harvestable_forest_c) @@ -645,20 +643,10 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentCohort%npp_acc_hold = currentCohort%npp_acc_hold - & currentCohort%resp_excess_hold*real( hlm_days_per_year,r8) - - ! Passing gpp_acc_hold to HLM - bc_out%gpp_site = bc_out%gpp_site + currentCohort%gpp_acc_hold * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day - bc_out%ar_site = bc_out%ar_site + (currentCohort%resp_m_acc_hold + & - currentCohort%resp_g_acc_hold + currentCohort%resp_excess_hold*real(hlm_days_per_year,r8) ) * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day ! Update the mass balance tracking for the daily nutrient uptake flux ! Then zero out the daily uptakes, they have been used - ! ----------------------------------------------------------------------------- - - call EffluxIntoLitterPools(currentSite, currentPatch, currentCohort, bc_in ) @@ -693,7 +681,9 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentCohort%resp_m_acc*currentCohort%n + & currentCohort%resp_excess_hold*currentCohort%n + & currentCohort%resp_g_acc_hold*currentCohort%n/real( hlm_days_per_year,r8) - + + + call currentCohort%prt%CheckMassConservation(ft,5) ! Update the leaf biophysical rates based on proportion of leaf @@ -925,11 +915,16 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) bc_out%seed_c_si = bc_out%seed_c_si * g_per_kg * AREA_INV ! Set boundary condition to HLM for carbon loss to atm from fires and grazing - ! [kgC/ha/day]*[m2/ha]*[day/s] = [kg/m2/s] + ! [kgC/ha/day]*[ha/m2]*[day/s] = [kg/m2/s] site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * ha_per_m2 * days_per_sec + ! Pass site-level mass fluxes to output boundary conditions + ! [kg/site/day] * [site/m2 day/sec] = [kgC/m2/s] + bc_out%gpp_site = site_cmass%gpp_acc * area_inv / sec_per_day + bc_out%ar_site = site_cmass%aresp_acc * area_inv / sec_per_day + end subroutine ed_update_site !-------------------------------------------------------------------------------! @@ -1178,14 +1173,6 @@ subroutine bypass_dynamics(currentSite, bc_out) ! Shouldn't need to zero any nutrient fluxes ! as they should just be zero, no uptake ! in ST3 mode. - - ! Passing - bc_out%gpp_site = bc_out%gpp_site + currentCohort%gpp_acc_hold * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day - bc_out%ar_site = bc_out%ar_site + (currentCohort%resp_m_acc_hold + & - currentCohort%resp_g_acc_hold + & - currentCohort%resp_excess_hold*real( hlm_days_per_year,r8)) * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day currentCohort => currentCohort%taller enddo diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 09d420fa75..178f0f4475 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -2698,40 +2698,4 @@ subroutine FatesReadParameters(param_reader) end subroutine FatesReadParameters -! ====================================================================================== - -subroutine RestartUpdateBCOut(this, s) - - ! Arguments - class(fates_interface_type), intent(inout) :: this - integer, intent(in) :: s - - ! Locals - type(fates_patch_type), pointer :: currentPatch - type(fates_cohort_type), pointer :: currentCohort - - ! Zero gpp and ar as the update call does not zero - this%bc_out(s)%gpp_site = 0._r8 - this%bc_out(s)%ar_site = 0._r8 - - currentPatch => this%sites(s)%youngest_patch - do while(associated(currentPatch)) - currentCohort => currentPatch%shortest - do while(associated(currentCohort)) - - if (.not. currentCohort%isnew) then - - call this%bc_out(s)%UpdateGPPAR(currentCohort%n, currentCohort%gpp_acc_hold, & - currentCohort%resp_g_acc_hold, currentCohort%resp_m_acc_hold, & - currentCohort%resp_excess_hold, sec_per_day, area_inv) - - end if - - currentCohort => currentCohort%taller - end do - currentPatch => currentPatch%older - end do - -end subroutine RestartUpdateBCOut - end module FatesInterfaceMod diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index af8f208de0..4f27f90805 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -804,10 +804,6 @@ module FatesInterfaceTypesMod real(r8) :: litter_cwd_c_si ! Total litter plus CWD carbon [Site-Level, gC m-2] real(r8) :: seed_c_si ! Total seed carbon [Site-Level, gC m-2] - contains - - procedure :: UpdateGPPAR - end type bc_out_type @@ -863,29 +859,4 @@ subroutine ZeroBCOutCarbonFluxes(bc_out) end subroutine ZeroBCOutCarbonFluxes - ! ====================================================================================== - - subroutine UpdateGPPAR(this, ncohorts, gpp_acc_hold, resp_g_acc_hold, resp_m_acc_hold, & - resp_excess_hold, sec_per_day, AREA_INV) - - class(bc_out_type), intent(inout) :: this - real(r8), intent(in) :: ncohorts - real(r8), intent(in) :: gpp_acc_hold - real(r8), intent(in) :: resp_g_acc_hold - real(r8), intent(in) :: resp_m_acc_hold - real(r8), intent(in) :: resp_excess_hold - real(r8), intent(in) :: sec_per_day - real(r8), intent(in) :: AREA_INV - - real(r8) :: conversion_factor - - conversion_factor = AREA_INV * ncohorts / real(hlm_days_per_year,r8) / sec_per_day - - this%gpp_site = this%gpp_site + gpp_acc_hold * conversion_factor - - this%ar_site = this%ar_site + (resp_m_acc_hold + resp_g_acc_hold + & - resp_excess_hold*real( hlm_days_per_year,r8)) * conversion_factor - - end subroutine UpdateGPPAR - end module FatesInterfaceTypesMod diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index f2c78051a1..6b89fa4d84 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -112,6 +112,9 @@ module FatesRestartInterfaceMod integer :: ir_snow_depth_si integer :: ir_trunk_product_si integer :: ir_landuse_config_si + integer :: ir_gpp_acc_si + integer :: ir_ar_acc_si + integer :: ir_ncohort_pa integer :: ir_canopy_layer_co integer :: ir_canopy_layer_yesterday_co @@ -316,7 +319,8 @@ module FatesRestartInterfaceMod ! The number of variable dim/kind types we have defined (static) integer, parameter, public :: fates_restart_num_dimensions = 2 !(cohort,column) - integer, parameter, public :: fates_restart_num_dim_kinds = 4 !(cohort-int,cohort-r8,site-int,site-r8) + integer, parameter, public :: fates_restart_num_dim_kinds = 4 !(cohort-int,cohort-r8, + ! site-int,site-r8) ! integer constants for storing logical data integer, parameter, public :: old_cohort = 0 @@ -749,6 +753,16 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/m2', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_landuse_config_si ) + call this%set_restart_var(vname='fates_massbal_gpp', vtype=site_r8, & + long_name='accumulated gpp over previous day cycle', & + units='kgC/m2/s', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_gpp_acc_si ) + + call this%set_restart_var(vname='fates_massbal_ar', vtype=site_r8, & + long_name='accumulated autotrophic respiration over previous day cycle', & + units='kgC/m2/s', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_ar_acc_si ) + ! ----------------------------------------------------------------------------------- ! Variables stored within cohort vectors ! Note: Some of these are multi-dimensional variables in the patch/site dimension @@ -2080,6 +2094,7 @@ subroutine set_restart_vectors(this,nc,nsites,sites) integer :: ft ! functional type index integer :: el ! element loop index + integer :: c_el ! element loop index for carbon12 integer :: ilyr ! soil layer index integer :: nlevsoil ! total soil layers in patch of interest integer :: k,j,i ! indices to the radiation matrix @@ -2378,7 +2393,9 @@ subroutine set_restart_vectors(this,nc,nsites,sites) end do end if - + c_el = element_pos(carbon12_element) + this%rvars(ir_gpp_acc_si)%r81d(io_idx_si) = sites(s)%mass_balance(c_el)%gpp_acc + this%rvars(ir_aresp_acc_si)%r81d(io_idx_si) = sites(s)%mass_balance(c_el)%aresp_acc ! canopy spread term rio_spread_si(io_idx_si) = sites(s)%spread @@ -3084,6 +3101,7 @@ subroutine get_restart_vectors(this, nc, nsites, sites) integer :: patchespersite ! number of patches per site integer :: cohortsperpatch ! number of cohorts per patch integer :: el ! loop counter for elements + integer :: c_el ! loop counter for carbon12 integer :: nlevsoil ! number of soil layers integer :: ilyr ! soil layer loop counter integer :: iscpf ! multiplex loop counter for size x pft @@ -3363,6 +3381,11 @@ subroutine get_restart_vectors(this, nc, nsites, sites) end do end if + + c_el = element_pos(carbon12_element) + sites(s)%mass_balance(c_el)%gpp_acc = this%rvars(ir_gpp_acc_si)%r81d(io_idx_si) + sites(s)%mass_balance(c_el)%aresp_acc = this%rvars(ir_aresp_acc_si)%r81d(io_idx_si) + sites(s)%spread = rio_spread_si(io_idx_si) From 58e0d670a261f881e763dd46ec2413e5b3ac356c Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Thu, 11 Sep 2025 10:22:53 -0700 Subject: [PATCH 159/194] bug fixes for changes to nbp bc_out variables --- main/FatesInterfaceMod.F90 | 4 ---- main/FatesRestartInterfaceMod.F90 | 7 ++++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 178f0f4475..06bbf3d0b7 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -160,10 +160,6 @@ module FatesInterfaceMod type(bc_pconst_type) :: bc_pconst - contains - - procedure :: RestartUpdateBCOut - end type fates_interface_type diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index 6b89fa4d84..2c0f9e7b1c 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -49,8 +49,9 @@ module FatesRestartInterfaceMod use EDTypesMod, only : area use EDTypesMod, only : set_patchno use EDParamsMod, only : nlevleaf - use PRTGenericMod, only : prt_global + use PRTGenericMod, only : carbon12_element use PRTGenericMod, only : num_elements + use PRTGenericMod, only : element_pos use FatesRunningMeanMod, only : rmean_type use FatesRunningMeanMod, only : ema_lpa use FatesRadiationMemMod, only : num_swb,norman_solver,twostr_solver @@ -113,7 +114,7 @@ module FatesRestartInterfaceMod integer :: ir_trunk_product_si integer :: ir_landuse_config_si integer :: ir_gpp_acc_si - integer :: ir_ar_acc_si + integer :: ir_aresp_acc_si integer :: ir_ncohort_pa integer :: ir_canopy_layer_co @@ -761,7 +762,7 @@ subroutine define_restart_vars(this, initialize_variables) call this%set_restart_var(vname='fates_massbal_ar', vtype=site_r8, & long_name='accumulated autotrophic respiration over previous day cycle', & units='kgC/m2/s', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_ar_acc_si ) + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_aresp_acc_si ) ! ----------------------------------------------------------------------------------- ! Variables stored within cohort vectors From f8d9e2bc93d49d431282b99e76e376ad466f28af Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 12 Sep 2025 11:30:56 -0700 Subject: [PATCH 160/194] Updated some descriptive text --- biogeochem/EDPatchDynamicsMod.F90 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index e6ba98b568..cdf104d587 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -1895,9 +1895,13 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt => currentPatch%litter(el) new_litt => newPatch%litter(el) - ! Distribute the fragmentation litter flux rates. This is only used for diagnostics - ! at this point. Litter fragmentation has already been passed to the output - ! boundary flux arrays. + ! Distribute the fragmentation litter flux rates. The mean site-level + ! flux rate must be preserved, so when we create new patches + ! from disturbance, we must area weight the contributions of the + ! donor patches. This is because the host model will call + ! FatesSoilBGCFluxMod:FluxIntoLitterPools() which uses these + ! litt%<>_frac() arrays to fill site level output fluxes, and + ! this is called over the next day on the model timestep. do c = 1,ncwd new_litt%ag_cwd_frag(c) = new_litt%ag_cwd_frag(c) + & From 638ae670d4b48b235e487a439940d7ffab9c74c1 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Sun, 14 Sep 2025 19:02:45 -0400 Subject: [PATCH 161/194] zeroing cmass%gpp for mass checking on restart --- main/EDMainMod.F90 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 81b8ea3bb7..8bc045a454 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -841,13 +841,23 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) real(r8) :: total_stock ! dummy variable for receiving from sitemassstock !----------------------------------------------------------------------- + site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) + ! check patch order (set second argument to true) if (debug) then call set_patchno(currentSite,.true.,1) end if + + ! Pass site-level mass fluxes to output boundary conditions + ! [kg/site/day] * [site/m2 day/sec] = [kgC/m2/s] + bc_out%gpp_site = site_cmass%gpp_acc * area_inv / sec_per_day + bc_out%ar_site = site_cmass%aresp_acc * area_inv / sec_per_day if(hlm_use_sp.eq.ifalse .and. (.not.is_restarting))then - call canopy_spread(currentSite) + call canopy_spread(currentSite) + else + site_cmass%gpp_acc = 0._r8 + site_cmass%aresp_acc = 0._r8 end if call TotalBalanceCheck(currentSite,6) @@ -916,14 +926,11 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! Set boundary condition to HLM for carbon loss to atm from fires and grazing ! [kgC/ha/day]*[ha/m2]*[day/s] = [kg/m2/s] - site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) + bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * ha_per_m2 * days_per_sec - ! Pass site-level mass fluxes to output boundary conditions - ! [kg/site/day] * [site/m2 day/sec] = [kgC/m2/s] - bc_out%gpp_site = site_cmass%gpp_acc * area_inv / sec_per_day - bc_out%ar_site = site_cmass%aresp_acc * area_inv / sec_per_day + end subroutine ed_update_site From 461d96faaf60cb66adb1a8c04b162b53098cde44 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 16 Sep 2025 13:57:04 -0700 Subject: [PATCH 162/194] update the README link to the compatibility table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3129042304..0c42a2a5c9 100644 --- a/README.md +++ b/README.md @@ -35,4 +35,4 @@ https://github.com/E3SM-Project/E3SM https://github.com/ESCOMP/cesm -The FATES, E3SM and CTSM teams maintain compatability of the NGEET/FATES master branch with the E3SM master and CTSM master branches respectively. There may be some modest lag time in which the latest commit on the FATES master branch is available to these host land models (HLM) by default. This is typically correlated with FATES development updates forcing necessary changes to the FATES API. See the table of [FATES API/HLM compatibility](https://fates-users-guide.readthedocs.io/en/latest/user/Table-of-FATES-API-and-HLM-STATUS.html) for information on which fates tag corresponds to which HLM tag or commit. +The FATES, E3SM and CTSM teams maintain compatability of the NGEET/FATES master branch with the E3SM master and CTSM master branches respectively. There may be some modest lag time in which the latest commit on the FATES master branch is available to these host land models (HLM) by default. This is typically correlated with FATES development updates forcing necessary changes to the FATES API. See the table of [FATES API/HLM compatibility](https://fates-users-guide.readthedocs.io/en/latest/user/release-tags-compat-table.html) for information on which fates tag corresponds to which HLM tag or commit. From a0a1f037cec32377ee4a982fad750ead045d0c06 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 16 Sep 2025 13:59:07 -0700 Subject: [PATCH 163/194] update the other readme link to hlm compat table link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c42a2a5c9..de06aa0e5b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To receive email updates about forthcoming release tags, regular meeting notific [How to Contribute](https://github.com/NGEET/fates/blob/master/CONTRIBUTING.md) -[Table of FATES and Host Land Model API compatability](https://fates-users-guide.readthedocs.io/en/latest/user/Table-of-FATES-API-and-HLM-STATUS.html) +[Table of FATES and Host Land Model API compatability](https://fates-users-guide.readthedocs.io/en/latest/user/release-tags-compat-table.html) [List of Unsupported or Broken Features](https://fates-users-guide.readthedocs.io/en/latest/user/Current-Unsupported-or-Broken-Features.html) From 2f6b9371831a40844f7a273f91d4a1ceb5cfa572 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 16 Sep 2025 14:29:27 -0700 Subject: [PATCH 164/194] updating old wiki references to point to the user's guide --- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 45f5bce859..f41cc246f4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -53,4 +53,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ -[wiki_ref_page]: https://github.com/NGEET/fates/wiki/Relevant-References +[references]: https://fates-users-guide.readthedocs.io/en/latest/user/Relevant-References.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7aa26a795..532368d119 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,11 +12,11 @@ https://github.com/NGEET/fates/blob/master/CODE_OF_CONDUCT.md ## Getting Started -Those who wish to contribute code to FATES must have those changes integrated through the developer repository NGEET/fates. Changes that make it to public releases must go through this repository first, as well. Here are some basic first steps. +Those who wish to contribute code to FATES must have those changes integrated through the developer repository NGEET/fates. Changes that make it to public releases must go through this repository first, as well. Please refer to the [developer section](hhttps://fates-users-guide.readthedocs.io/en/latest/developer/developer-guide.html) of the [User's Guide](https://fates-users-guide.readthedocs.io/en/latest/index.html) for more details. Here are some basic first steps: * All developers should create a fork of the NGEET/fates repository into their personal space on github -* Follow the developer work-flow described here: https://github.com/NGEET/fates/wiki/FATES-Development-Workflow -* Each set of changes should have its own feature branch that encapsulates your desired changes, following the conventions outlined here: https://github.com/NGEET/fates/wiki/Feature-Branch-Naming-Convention +* Follow the [developer work-flow](https://fates-users-guide.readthedocs.io/en/latest/developer/FATES-Development-Workflow.html) +* Each set of changes should have its own [feature branch](https://fates-users-guide.readthedocs.io/en/latest/developer/Feature-Branch-Naming-Convention.html) that encapsulates your desired changes. * The work-flow will lead you eventually to submit a Pull-Request to NGEET/fates:master, please follow the template in the Pull Request and communicate as best you can if you are unsure how to fill out the text * It is best to create an issue to describe the work you are undertaking prior to starting. This helps the community sync with your efforts, prevents duplication of efforts, and science is not done in a vaccuum! * Expect peers to interact, help, discuss and eventually approve your submission (pull-request) @@ -31,11 +31,11 @@ In addition to the github discussions, we hold a roughly biweekly call, which co * Changes that are submitted should be limited to 1 single feature (i.e. don't submit changes to the radiation code and the nutrient cycle simultaneous, pick one thing) * Check for unnecessary whitespace with `git diff --check` before committing * We have no standard protocol for commit messages, but try to make them meaningful, concise and succinct. -* You will most likely have to test (see workflow above), see: https://github.com/NGEET/fates/wiki/Testing-Protocols +* You will most likely have to test (see workflow above), see the [testing protocols](https://fates-users-guide.readthedocs.io/en/latest/developer/Testing-Protocols.html) for more details. ## Coding Practices and Style -Please refer to the FATES style guide: https://github.com/NGEET/fates/wiki/Coding-Practices-and-Style-Guide +Please refer to the FATES [style guide](https://fates-users-guide.readthedocs.io/en/latest/developer/style.html) ## Trivial Changes From 0ba914cf304fd963ad9f6234e94ca784f3d4801f Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 23 Sep 2025 11:40:23 -0400 Subject: [PATCH 165/194] Updated logic on restarts to assume newly calculated stocks should match the saved stocks, ie zero flux --- main/EDMainMod.F90 | 63 +++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 7a5bd21840..11ed263eaa 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -198,7 +198,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) call ZeroBCOutCarbonFluxes(bc_out) ! Zero mass balance - call TotalBalanceCheck(currentSite, 0) + call TotalBalanceCheck(currentSite, 0, is_restarting=.false.) ! We do not allow phenology while in ST3 mode either, it is hypothetically ! possible to allow this, but we have not plugged in the litter fluxes @@ -263,7 +263,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) currentPatch => currentPatch%younger enddo - call TotalBalanceCheck(currentSite,1) + call TotalBalanceCheck(currentSite,1,is_restarting=.false.) currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -286,7 +286,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) end if - call TotalBalanceCheck(currentSite,2) + call TotalBalanceCheck(currentSite,2,is_restarting=.false.) !********************************************************************************* ! Patch dynamics sub-routines: fusion, new patch creation (spwaning), termination. @@ -304,7 +304,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) call spawn_patches(currentSite, bc_in) - call TotalBalanceCheck(currentSite,3) + call TotalBalanceCheck(currentSite,3,is_restarting=.false.) ! fuse on the spawned patches. call fuse_patches(currentSite, bc_in ) @@ -319,14 +319,14 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) end if ! SP has changes in leaf carbon but we don't expect them to be in balance. - call TotalBalanceCheck(currentSite,4) + call TotalBalanceCheck(currentSite,4,is_restarting=.false.) ! kill patches that are too small call terminate_patches(currentSite, bc_in) end if ! Final instantaneous mass balance check - call TotalBalanceCheck(currentSite,5) + call TotalBalanceCheck(currentSite,5,is_restarting=.false.) end subroutine ed_ecosystem_dynamics @@ -860,13 +860,13 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) site_cmass%aresp_acc = 0._r8 end if - call TotalBalanceCheck(currentSite,6) + call TotalBalanceCheck(currentSite,6,is_restarting=is_restarting) if(hlm_use_sp.eq.ifalse .and. (.not.is_restarting) )then call canopy_structure(currentSite, bc_in) endif - call TotalBalanceCheck(currentSite,final_check_id) + call TotalBalanceCheck(currentSite,final_check_id,is_restarting=is_restarting) ! Update recruit L2FRs based on new canopy position call SetRecruitL2FR(currentSite) @@ -936,17 +936,25 @@ end subroutine ed_update_site !-------------------------------------------------------------------------------! - subroutine TotalBalanceCheck (currentSite, call_index ) + subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! ! !DESCRIPTION: ! This routine looks at the mass flux in and out of the FATES and compares it to ! the change in total stocks (states). ! Fluxes in are NPP. Fluxes out are decay of CWD and litter into SOM pools. + ! Note: If the model is restarting, it is assumed that the mass stocks + ! that were saved in the restart file are the "old" stocks, and they + ! should equal the stocks that are currently in the sites. However, + ! the fluxes on a restart are saved so that they can inform the HLM + ! on the next day, so we have to modify our mass balance check to ignore + ! fluxes on restarts. ! ! !ARGUMENTS: type(ed_site_type) , intent(inout) :: currentSite integer , intent(in) :: call_index + logical , intent(in) :: is_restarting + ! ! !LOCAL VARIABLES: type(site_massbal_type),pointer :: site_mass @@ -987,7 +995,6 @@ subroutine TotalBalanceCheck (currentSite, call_index ) change_in_stock = 0.0_r8 - ! Loop through the number of elements in the system do el = 1, num_elements @@ -997,22 +1004,26 @@ subroutine TotalBalanceCheck (currentSite, call_index ) call SiteMassStock(currentSite,el,total_stock,biomass_stock,litter_stock,seed_stock) change_in_stock = total_stock - site_mass%old_stock - - flux_in = site_mass%seed_in + & - site_mass%net_root_uptake + & - site_mass%gpp_acc + & - site_mass%flux_generic_in + & - site_mass%patch_resize_err - - flux_out = sum(site_mass%wood_product_harvest(:)) + & - sum(site_mass%wood_product_landusechange(:)) + & - site_mass%burn_flux_to_atm + & - site_mass%seed_out + & - site_mass%flux_generic_out + & - site_mass%frag_out + & - site_mass%aresp_acc + & - site_mass%herbivory_flux_out - + if(is_restarting) then + flux_in = 0._r8 + flux_out = 0._r8 + else + flux_in = site_mass%seed_in + & + site_mass%net_root_uptake + & + site_mass%gpp_acc + & + site_mass%flux_generic_in + & + site_mass%patch_resize_err + + flux_out = sum(site_mass%wood_product_harvest(:)) + & + sum(site_mass%wood_product_landusechange(:)) + & + site_mass%burn_flux_to_atm + & + site_mass%seed_out + & + site_mass%flux_generic_out + & + site_mass%frag_out + & + site_mass%aresp_acc + & + site_mass%herbivory_flux_out + end if + net_flux = flux_in - flux_out error = abs(net_flux - change_in_stock) From a7c739a21f30b9a2bc568a9e4cda61d3fa05a643 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 23 Sep 2025 14:52:13 -0400 Subject: [PATCH 166/194] More consistency in site level mass flux accounting and restarting --- main/EDMainMod.F90 | 31 ++++++++--------------- main/EDTypesMod.F90 | 21 +++++----------- main/FatesRestartInterfaceMod.F90 | 42 +++++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 11ed263eaa..8d75256e50 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -668,11 +668,6 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentSite%mass_balance(element_pos(carbon12_element))%net_root_uptake - & currentCohort%daily_c_efflux*currentCohort%n - ! Save NPP diagnostic for flux accounting [kg/m2/day] - - currentSite%flux_diags%npp = currentSite%flux_diags%npp + & - currentCohort%npp_acc_hold/real( hlm_days_per_year,r8) * currentCohort%n * area_inv - ! And simultaneously add the input fluxes to mass balance accounting site_cmass%gpp_acc = site_cmass%gpp_acc + & currentCohort%gpp_acc * currentCohort%n @@ -682,8 +677,6 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentCohort%resp_excess_hold*currentCohort%n + & currentCohort%resp_g_acc_hold*currentCohort%n/real( hlm_days_per_year,r8) - - call currentCohort%prt%CheckMassConservation(ft,5) ! Update the leaf biophysical rates based on proportion of leaf @@ -848,10 +841,7 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) call set_patchno(currentSite,.true.,1) end if - ! Pass site-level mass fluxes to output boundary conditions - ! [kg/site/day] * [site/m2 day/sec] = [kgC/m2/s] - bc_out%gpp_site = site_cmass%gpp_acc * area_inv / sec_per_day - bc_out%ar_site = site_cmass%aresp_acc * area_inv / sec_per_day + if(hlm_use_sp.eq.ifalse .and. (.not.is_restarting))then call canopy_spread(currentSite) @@ -927,10 +917,10 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! Set boundary condition to HLM for carbon loss to atm from fires and grazing ! [kgC/ha/day]*[ha/m2]*[day/s] = [kg/m2/s] - bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec - bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * ha_per_m2 * days_per_sec - - + bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * area_inv * days_per_sec + bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * area_inv * days_per_sec + bc_out%gpp_site = site_cmass%gpp_acc * area_inv * days_per_sec + bc_out%ar_site = site_cmass%aresp_acc * area_inv * days_per_sec end subroutine ed_update_site @@ -997,7 +987,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! Loop through the number of elements in the system - do el = 1, num_elements + do_elem_loop: do el = 1, num_elements site_mass => currentSite%mass_balance(el) @@ -1121,12 +1111,13 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! This is the last check of the sequence, where we update our total ! error check and the final fates stock - if(call_index == final_check_id) then - site_mass%old_stock = total_stock - site_mass%err_fates = net_flux - change_in_stock + if(call_index == final_check_id .and. .not.is_restarting) then + site_mass%old_stock = total_stock + site_mass%err_fates = net_flux - change_in_stock end if - end do + end do do_elem_loop + end if ! not SP mode end subroutine TotalBalanceCheck diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index 90be4df5ec..46c65218b1 100644 --- a/main/EDTypesMod.F90 +++ b/main/EDTypesMod.F90 @@ -217,18 +217,15 @@ module EDTypesMod type, public :: site_fluxdiags_type - ! This is for all diagnostics that are uniform over all elements (C,N,P) + ! These are site level flux diagnostics that are not used + ! in mass balance checks. We use these structures + ! to inform the history output. These values are not + ! zero'd when dynamics are completed. These values + ! are zero'd on cold-starts, and on restarts prior to the read + ! This is for all diagnostics that are uniform over all elements (C,N,P) type(elem_diag_type), pointer :: elem(:) - ! This variable is slated as to-do, but the fluxdiags type needs - ! to be refactored first. Currently this type is allocated - ! by chemical species (ie C, N or P). GPP is C, but not N or P (RGK 0524) - ! Previous day GPP [kgC/m2/year], partitioned by size x pft - !real(r8),allocatable :: gpp_prev_scpf(:) - - real(r8) :: npp ! kg m-2 day-1 - ! Nutrient Flux Diagnostics real(r8) :: resp_excess ! plant carbon respired due to carbon overflow @@ -678,7 +675,6 @@ subroutine ZeroFluxDiags(this) end do - this%npp = 0._r8 this%resp_excess = 0._r8 this%nh4_uptake = 0._r8 this%no3_uptake = 0._r8 @@ -694,11 +690,6 @@ subroutine ZeroFluxDiags(this) this%p_uptake_scpf(:) = 0._r8 this%p_efflux_scpf(:) = 0._r8 - ! We don't zero gpp_prev_scpf because this is not - ! incremented like others, it is assigned at the end - ! of the daily history write process - - return end subroutine ZeroFluxDiags diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index fb8deaced4..82835aa27d 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -114,6 +114,8 @@ module FatesRestartInterfaceMod integer :: ir_landuse_config_si integer :: ir_gpp_acc_si integer :: ir_aresp_acc_si + integer :: ir_herbivory_flux_out_si + integer :: ir_burn_flux_to_atm_si integer :: ir_ncohort_pa integer :: ir_canopy_layer_co @@ -1199,6 +1201,15 @@ subroutine define_restart_vars(this, initialize_variables) units='kg/ha', veclength=num_elements, flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_errfates_mbal) + call this%RegisterCohortVector(symbol_base='herbivory_flux_out', vtype=site_r8, & + long_name_base='Mass flux of herbivory losses at the site level', & + units='kg/ha/day', veclength=num_elements, flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_herbivory_flux_out_si) + + call this%RegisterCohortVector(symbol_base='burn_flux_to_atm', vtype=site_r8, & + long_name_base='Mass flux of burn loss to the atmosphere at site level', & + units='kg/ha/day', veclength=num_elements, flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_burn_flux_to_atm_si) ! Time integrated mass balance accounting [kg/m2] call this%RegisterCohortVector(symbol_base='fates_liveveg_intflux', vtype=site_r8, & @@ -2543,7 +2554,8 @@ subroutine set_restart_vectors(this,nc,nsites,sites) do i_lu_donor = 1, n_landuse_cats do i_lu_receiver = 1, n_landuse_cats do i_dist = 1, n_dist_types - rio_disturbance_rates_siluludi(io_idx_si_luludi) = sites(s)%disturbance_rates(i_dist,i_lu_donor, i_lu_receiver) + rio_disturbance_rates_siluludi(io_idx_si_luludi) = & + sites(s)%disturbance_rates(i_dist,i_lu_donor, i_lu_receiver) io_idx_si_luludi = io_idx_si_luludi + 1 end do end do @@ -2557,19 +2569,30 @@ subroutine set_restart_vectors(this,nc,nsites,sites) io_idx_si_scpf = io_idx_co_1st do i_cwd=1,ncwd - this%rvars(ir_cwdagin_flxdg+el-1)%r81d(io_idx_si_cwd) = sites(s)%flux_diags%elem(el)%cwd_ag_input(i_cwd) - this%rvars(ir_cwdbgin_flxdg+el-1)%r81d(io_idx_si_cwd) = sites(s)%flux_diags%elem(el)%cwd_bg_input(i_cwd) + this%rvars(ir_cwdagin_flxdg+el-1)%r81d(io_idx_si_cwd) = & + sites(s)%flux_diags%elem(el)%cwd_ag_input(i_cwd) + this%rvars(ir_cwdbgin_flxdg+el-1)%r81d(io_idx_si_cwd) = & + sites(s)%flux_diags%elem(el)%cwd_bg_input(i_cwd) io_idx_si_cwd = io_idx_si_cwd + 1 end do do i_pft=1,numpft - this%rvars(ir_leaflittin_flxdg+el-1)%r81d(io_idx_si_pft) = sites(s)%flux_diags%elem(el)%surf_fine_litter_input(i_pft) - this%rvars(ir_rootlittin_flxdg+el-1)%r81d(io_idx_si_pft) = sites(s)%flux_diags%elem(el)%root_litter_input(i_pft) - this%rvars(ir_woodprod_harvest_mbal+el-1)%r81d(io_idx_si_pft) = sites(s)%mass_balance(el)%wood_product_harvest(i_pft) - this%rvars(ir_woodprod_landusechange_mbal+el-1)%r81d(io_idx_si_pft) = sites(s)%mass_balance(el)%wood_product_landusechange(i_pft) + this%rvars(ir_leaflittin_flxdg+el-1)%r81d(io_idx_si_pft) = & + sites(s)%flux_diags%elem(el)%surf_fine_litter_input(i_pft) + this%rvars(ir_rootlittin_flxdg+el-1)%r81d(io_idx_si_pft) = & + sites(s)%flux_diags%elem(el)%root_litter_input(i_pft) + this%rvars(ir_woodprod_harvest_mbal+el-1)%r81d(io_idx_si_pft) = & + sites(s)%mass_balance(el)%wood_product_harvest(i_pft) + this%rvars(ir_woodprod_landusechange_mbal+el-1)%r81d(io_idx_si_pft) = & + sites(s)%mass_balance(el)%wood_product_landusechange(i_pft) io_idx_si_pft = io_idx_si_pft + 1 end do + this%rvars(ir_herbivory_flux_out_si+el-1)%r81d(io_idx_si) = & + sites(s)%mass_balance(el)%herbivory_flux_out + this%rvars(ir_burn_flux_to_atm_si+el-1)%r81d(io_idx_si) = & + sites(s)%mass_balance(el)%burn_flux_to_atm + this%rvars(ir_oldstock_mbal+el-1)%r81d(io_idx_si) = sites(s)%mass_balance(el)%old_stock this%rvars(ir_errfates_mbal+el-1)%r81d(io_idx_si) = sites(s)%mass_balance(el)%err_fates @@ -3607,6 +3630,11 @@ subroutine get_restart_vectors(this, nc, nsites, sites) io_idx_si_pft = io_idx_si_pft + 1 end do + sites(s)%mass_balance(el)%herbivory_flux_out = & + this%rvars(ir_herbivory_flux_out_si+el-1)%r81d(io_idx_si) + sites(s)%mass_balance(el)%burn_flux_to_atm = & + this%rvars(ir_burn_flux_to_atm_si+el-1)%r81d(io_idx_si) + sites(s)%mass_balance(el)%old_stock = this%rvars(ir_oldstock_mbal+el-1)%r81d(io_idx_si) sites(s)%mass_balance(el)%err_fates = this%rvars(ir_errfates_mbal+el-1)%r81d(io_idx_si) From 2c9a15ebba1c465b184bce1ee099547da4d09356 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 23 Sep 2025 14:12:18 -0700 Subject: [PATCH 167/194] removed unecessary usage of fluxdiags%npp --- main/ChecksBalancesMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/ChecksBalancesMod.F90 b/main/ChecksBalancesMod.F90 index 325a1089e8..5fb0f7ccf8 100644 --- a/main/ChecksBalancesMod.F90 +++ b/main/ChecksBalancesMod.F90 @@ -302,7 +302,7 @@ subroutine CheckIntegratedMassPools(site) select case(element_list(el)) case(carbon12_element) - net_uptake = diag%npp + site_mass%net_root_uptake*area_inv + net_uptake = (site_mass%gpp_acc + site_mass%aresp_acc + site_mass%net_root_uptake)*area_inv case(nitrogen_element) net_uptake = site_mass%net_root_uptake*area_inv case(phosphorus_element) From f7365ba5ac06c111d758ccfe5c63998df908a988 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Tue, 30 Sep 2025 12:49:37 -0700 Subject: [PATCH 168/194] fix calculation of the distance between model grid and inventory sites by accounting for potential coords format mismatch --- main/FatesInventoryInitMod.F90 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/main/FatesInventoryInitMod.F90 b/main/FatesInventoryInitMod.F90 index 6673f4b819..5e31d9d749 100644 --- a/main/FatesInventoryInitMod.F90 +++ b/main/FatesInventoryInitMod.F90 @@ -157,6 +157,8 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) real(r8), allocatable :: inv_lat_list(:) ! list of lat coords real(r8), allocatable :: inv_lon_list(:) ! list of lon coords + real(r8), allocatable :: delta_lon_list(:) ! list of lon cord difference between model grid and inventory sites [0-180] + real(r8), allocatable :: dist_list(:) ! list of distance between model grid and inventory sites integer :: invsite ! index of inventory site ! closest to actual site integer :: el ! loop counter for number of elements @@ -231,13 +233,14 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) ! For each site, identify the most proximal PSS/CSS couplet, read-in the data ! allocate linked lists and assign to memory do s = 1, nsites - invsite = & - minloc( (sites(s)%lat-inv_lat_list(:))**2.0_r8 + & - (sites(s)%lon-inv_lon_list(:))**2.0_r8 , dim=1) + delta_lon_list = abs(modulo((sites(s)%lon - inv_lon_list(:)) + & + 180.0_r8, 360.0_r8)-180.0_r8) + dist_list = (sites(s)%lat - inv_lat_list(:))**2.0_r8 + & + delta_lon_list**2.0_r8 + invsite = minloc(dist_list(:), dim=1) ! Do a sanity check on the distance separation between physical site and model site - if ( sqrt( (sites(s)%lat-inv_lat_list(invsite))**2.0_r8 + & - (sites(s)%lon-inv_lon_list(invsite))**2.0_r8 ) > max_site_adjacency_deg ) then + if ( sqrt(dist_list(invsite)) ) > max_site_adjacency_deg ) then write(fates_log(), *) 'Model site at lat:',sites(s)%lat,' lon:',sites(s)%lon write(fates_log(), *) 'has no reasonably proximal site in the inventory site list.' write(fates_log(), *) 'Closest is at lat:',inv_lat_list(invsite),' lon:',inv_lon_list(invsite) From 78a9cece09668421a86c6da7ffa42238f10dd302 Mon Sep 17 00:00:00 2001 From: Xiulin Gao Date: Tue, 30 Sep 2025 13:11:34 -0700 Subject: [PATCH 169/194] correct wind speed unit for Rxfire burn window and NI --- fire/SFFireWeatherMod.F90 | 2 +- fire/SFNesterovMod.F90 | 2 +- parameter_files/fates_params_default.cdl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 37bc45eed8..8609912e37 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -78,7 +78,7 @@ subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up real(r8), intent(in) :: temp_C ! daily averaged temperature [degrees C] integer, intent(in) :: rxfire_switch ! whether prescribed fire is turned on real(r8), intent(in) :: rh ! daily relative humidity [%] - real(r8), intent(in) :: wind ! wind speed [m/min] + real(r8), intent(in) :: wind ! wind speed [m/s] real(r8), intent(in) :: temp_up ! user defined upper bound for temp when define a burn window real(r8), intent(in) :: temp_low ! user defined lower bound for temp when define a burn window real(r8), intent(in) :: rh_up ! user defined upper bound for relative humidity diff --git a/fire/SFNesterovMod.F90 b/fire/SFNesterovMod.F90 index 2a0147058b..3a75a8b2e2 100644 --- a/fire/SFNesterovMod.F90 +++ b/fire/SFNesterovMod.F90 @@ -49,7 +49,7 @@ subroutine update_nesterov_index(this, temp_C, precip, rh, wind) real(r8), intent(in) :: temp_C ! daily averaged temperature [degrees C] real(r8), intent(in) :: precip ! daily precipitation [mm] real(r8), intent(in) :: rh ! daily relative humidity [%] - real(r8), intent(in) :: wind ! daily wind speed [m/min] + real(r8), intent(in) :: wind ! daily wind speed [m/s] ! LOCALS: real(r8) :: t_dew ! dewpoint temperature [degrees C] diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index e899d3ff7b..1380a662e5 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -919,10 +919,10 @@ variables: fates_rxfire_temp_upthreshold:units = "degree C" ; fates_rxfire_temp_upthreshold:long_name = "maximum temprature threshold above which prescribed fire is disallowed" ; double fates_rxfire_wind_lwthreshold ; - fates_rxfire_wind_lwthreshold:units = "%" ; + fates_rxfire_wind_lwthreshold:units = "m/s" ; fates_rxfire_wind_lwthreshold:long_name = "minimum wind speed threshold below which prescribed fire is disallowed" ; double fates_rxfire_wind_upthreshold ; - fates_rxfire_wind_upthreshold:units = "%" ; + fates_rxfire_wind_upthreshold:units = "m/s" ; fates_rxfire_wind_upthreshold:long_name = "maximum wind speed threshold above which prescribed fire is disallowed" ; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; From 753e3018ea1ed0240a00b1e6f54b9bb32877f64e Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 10 Oct 2025 13:43:27 -0400 Subject: [PATCH 170/194] using great circle for calculating site/inventory adjacency, added unit tests --- main/FatesInventoryInitMod.F90 | 30 +++++++++----- main/FatesUtilsMod.F90 | 13 ++++-- testing/great_circle/TestGreatCircle.F90 | 34 ++++++++++++++++ testing/great_circle/WrapGreatCircleMod.F90 | 45 +++++++++++++++++++++ testing/great_circle/bld/README | 1 + testing/great_circle/build_gc.sh | 27 +++++++++++++ 6 files changed, 135 insertions(+), 15 deletions(-) create mode 100644 testing/great_circle/TestGreatCircle.F90 create mode 100644 testing/great_circle/WrapGreatCircleMod.F90 create mode 100644 testing/great_circle/bld/README create mode 100755 testing/great_circle/build_gc.sh diff --git a/main/FatesInventoryInitMod.F90 b/main/FatesInventoryInitMod.F90 index 5e31d9d749..32f5c6714b 100644 --- a/main/FatesInventoryInitMod.F90 +++ b/main/FatesInventoryInitMod.F90 @@ -77,6 +77,7 @@ module FatesInventoryInitMod use FatesConstantsMod, only : fates_unset_int use EDCanopyStructureMod, only : canopy_summarization, canopy_structure use FatesRadiationMemMod, only : num_swb + use FatesUtilsMod, only : GreatCircleDist implicit none private @@ -105,6 +106,9 @@ module FatesInventoryInitMod ! defined in model memory and a physical ! site listed in the file + real(r8), parameter :: max_site_adjacency_m = 5500._r8 ! 0.05 deg roughly equals 5.5k meters + ! at the two tropic lines (111 km/deg) + logical, parameter :: do_inventory_out = .false. @@ -148,6 +152,7 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) real(r8) :: age_init ! dummy value for creating a patch real(r8) :: area_init ! dummy value for creating a patch integer :: s ! site index + integer :: i ! inventory site index integer :: ipa ! patch index integer :: iv, ft, ic integer :: total_cohorts ! cohort counter for error checking @@ -157,8 +162,7 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) real(r8), allocatable :: inv_lat_list(:) ! list of lat coords real(r8), allocatable :: inv_lon_list(:) ! list of lon coords - real(r8), allocatable :: delta_lon_list(:) ! list of lon cord difference between model grid and inventory sites [0-180] - real(r8), allocatable :: dist_list(:) ! list of distance between model grid and inventory sites + real(r8), allocatable :: delta_site_list(:) ! list of differences between model site and inv site (m) integer :: invsite ! index of inventory site ! closest to actual site integer :: el ! loop counter for number of elements @@ -212,7 +216,7 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) allocate(inv_css_list(nfilesites)) allocate(inv_lat_list(nfilesites)) allocate(inv_lon_list(nfilesites)) - + allocate(delta_site_list(nfilesites)) ! Check through the sites that are listed and do some sanity checks ! ------------------------------------------------------------------------------------------ @@ -233,18 +237,22 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) ! For each site, identify the most proximal PSS/CSS couplet, read-in the data ! allocate linked lists and assign to memory do s = 1, nsites - delta_lon_list = abs(modulo((sites(s)%lon - inv_lon_list(:)) + & - 180.0_r8, 360.0_r8)-180.0_r8) - dist_list = (sites(s)%lat - inv_lat_list(:))**2.0_r8 + & - delta_lon_list**2.0_r8 - invsite = minloc(dist_list(:), dim=1) + + do i = 1,nfilesites + ! Great circle calculates the distance in meters between two points + ! on the earth and also factors in the earth's curvature + delta_site_list(i) = & + GreatCircleDist(sites(s)%lon,inv_lon_list(i),sites(s)%lat,inv_lat_list(i)) + end do + + invsite = minloc(delta_site_list(:), dim=1) ! Do a sanity check on the distance separation between physical site and model site - if ( sqrt(dist_list(invsite)) ) > max_site_adjacency_deg ) then + if ( delta_site_list(invsite) > max_site_adjacency_m ) then write(fates_log(), *) 'Model site at lat:',sites(s)%lat,' lon:',sites(s)%lon write(fates_log(), *) 'has no reasonably proximal site in the inventory site list.' write(fates_log(), *) 'Closest is at lat:',inv_lat_list(invsite),' lon:',inv_lon_list(invsite) - write(fates_log(), *) 'Separation must be less than ',max_site_adjacency_deg,' degrees' + write(fates_log(), *) 'Separation must be less than ',max_site_adjacency_m,' meters' write(fates_log(), *) 'Exiting' call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -484,7 +492,7 @@ subroutine initialize_sites_by_inventory(nsites,sites,bc_in) end do - deallocate(inv_format_list, inv_pss_list, inv_css_list, inv_lat_list, inv_lon_list) + deallocate(inv_format_list, inv_pss_list, inv_css_list, inv_lat_list, inv_lon_list,delta_site_list) return end subroutine initialize_sites_by_inventory diff --git a/main/FatesUtilsMod.F90 b/main/FatesUtilsMod.F90 index 03537bd226..1bd644bf4c 100644 --- a/main/FatesUtilsMod.F90 +++ b/main/FatesUtilsMod.F90 @@ -7,7 +7,6 @@ module FatesUtilsMod use FatesGlobals, only : fates_log use FatesConstantsMod, only : nearzero use FatesGlobals, only : endrun => fates_endrun - use shr_log_mod , only : errMsg => shr_log_errMsg implicit none @@ -21,6 +20,7 @@ module FatesUtilsMod public :: QuadraticRootsNSWC public :: QuadraticRootsSridharachary public :: ArrayNint + public :: GreatCircleDist character(len=*), parameter, private :: sourcefile = & __FILE__ @@ -130,10 +130,15 @@ real(r8) function GreatCircleDist(slons,slonf,slats,slatf) real(r8) :: x real(r8) :: y !---------------------------------------------------------------------------------------! - + + ! ---- Make sure that longitudes are using the same convention (-180,180) + + lons = modulo(slons + 180.0_r8,360.0_r8)-180.0_r8 + lonf = modulo(slonf + 180.0_r8,360.0_r8)-180.0_r8 + !----- Convert the co-ordinates to double precision and to radians. --------------------! - lons = slons * rad_per_deg - lonf = slonf * rad_per_deg + lons = lons * rad_per_deg + lonf = lonf * rad_per_deg lats = slats * rad_per_deg latf = slatf * rad_per_deg dlon = lonf - lons diff --git a/testing/great_circle/TestGreatCircle.F90 b/testing/great_circle/TestGreatCircle.F90 new file mode 100644 index 0000000000..87316d84fa --- /dev/null +++ b/testing/great_circle/TestGreatCircle.F90 @@ -0,0 +1,34 @@ +program TestGreatCircle + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesUtilsMod, only : GreatCircleDist + implicit none + + ! Variable declarations + real(r8) :: inv_lat_list(5) ! list of lat coords + real(r8) :: inv_lon_list(5) ! list of lon coords + real(r8) :: site_lat_list(5) ! list of lat coords + real(r8) :: site_lon_list(5) ! list of lon coords + real(r8) :: delta_site_list(5) + + integer :: i,s + integer :: invsite + + inv_lat_list = (/-89._r8, -20._r8, 0._r8, 60._r8, 90._r8/) + inv_lon_list = (/-170._r8, -20._r8, 120.5_r8, 210._r8, 90._r8/) + + site_lat_list = (/-19._r8, -89._r8, 1._r8, 63._r8, 88._r8/) + site_lon_list = (/-21._r8, -171._r8, 118.5_r8, 214._r8, 78._r8/) + + do s=1,5 + do i =1,5 + delta_site_list(i) = & + GreatCircleDist(site_lon_list(s),inv_lon_list(i),site_lat_list(s),inv_lat_list(i)) + end do + invsite = minloc(delta_site_list(:), dim=1) + write(*,'(A,2(F6.1),A,2(F6.1))') "closest to ", site_lat_list(s),site_lon_list(s), & + " is: ",inv_lat_list(invsite),inv_lon_list(invsite) + write(*,'(A,F6.1,A)') " with distance of:",delta_site_list(invsite)/1000._r8," km" + end do + +end program TestGreatCircle diff --git a/testing/great_circle/WrapGreatCircleMod.F90 b/testing/great_circle/WrapGreatCircleMod.F90 new file mode 100644 index 0000000000..2728111c34 --- /dev/null +++ b/testing/great_circle/WrapGreatCircleMod.F90 @@ -0,0 +1,45 @@ +module shr_log_mod + use iso_c_binding, only : c_char + use iso_c_binding, only : c_int + + public :: shr_log_errMsg + +contains + function shr_log_errMsg(source, line) result(ans) + character(kind=c_char,len=*), intent(in) :: source + integer(c_int), intent(in) :: line + character(kind=c_char,len=4) :: cline ! character version of int + character(kind=c_char,len=128) :: ans + + write(cline,'(I4)') line + ans = "source: " // trim(source) // " line: "// trim(cline) + + end function shr_log_errMsg + +end module shr_log_mod + +module FatesGlobals + + use iso_c_binding, only : c_char + use iso_c_binding, only : c_int + use FatesConstantsMod, only : r8 => fates_r8 + + integer :: stdo_unit = 6 + +contains + + integer function fates_log() + fates_log = 6 + end function fates_log + + subroutine fates_endrun(msg) + + implicit none + character(len=*), intent(in) :: msg ! string to be printed + + write(stdo_unit,*) msg + + stop + + end subroutine fates_endrun +end module FatesGlobals diff --git a/testing/great_circle/bld/README b/testing/great_circle/bld/README new file mode 100644 index 0000000000..5954904f79 --- /dev/null +++ b/testing/great_circle/bld/README @@ -0,0 +1 @@ +folder holder diff --git a/testing/great_circle/build_gc.sh b/testing/great_circle/build_gc.sh new file mode 100755 index 0000000000..c3c065c7b5 --- /dev/null +++ b/testing/great_circle/build_gc.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Path to FATES src + +FC='gfortran' + +#F_OPTS="-fPIC -O3 -llapack" +F_OPTS="-g -fPIC" +F_OBJ_OPTS="-shared" + +FATES_PATH='../../' + +#F_OPTS="-fPIC -O0 -g -ffpe-trap=zero,overflow,underflow -fbacktrace -fbounds-check -Wall" + +MOD_FLAG="-J" + +rm -f bld/*.o +rm -f bld/*.so +rm -f bld/*.mod +rm -f bld/*.a + +# Build dgesv from lapack +${FC} ${F_OPTS} -c -I bld/ -J./bld/ -o bld/libFatesConstantsMod.so ${FATES_PATH}/main/FatesConstantsMod.F90 +${FC} ${F_OPTS} -c -I bld/ -J./bld/ -o bld/libWrapGreatCircleMod.so WrapGreatCircleMod.F90 +${FC} ${F_OPTS} -c -I bld/ -J./bld/ -o bld/libFatesUtilsMod.so ${FATES_PATH}/main/FatesUtilsMod.F90 +${FC} ${F_OPTS} -I bld/ -J./bld/ -L./bld/ -lFatesConstantsMod -lFatesUtilsMod -lWrapGreatCircleMod -o test_gc TestGreatCircle.F90 + From b4a1a10fedad2d35c58b6a5cc60bcfb8f767b0d5 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Fri, 10 Oct 2025 16:24:52 -0400 Subject: [PATCH 171/194] merge resolution --- .gitattributes | 7 +- .github/PULL_REQUEST_TEMPLATE.md | 3 + .gitignore | 5 +- CMakeLists.txt | 19 + CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 10 +- README.md | 4 +- biogeochem/EDCanopyStructureMod.F90 | 1424 ++++--------- biogeochem/EDCohortDynamicsMod.F90 | 47 +- biogeochem/EDLoggingMortalityMod.F90 | 3 - biogeochem/EDMortalityFunctionsMod.F90 | 9 +- biogeochem/EDPatchDynamicsMod.F90 | 150 +- biogeochem/EDPhysiologyMod.F90 | 205 +- biogeochem/FatesAllometryMod.F90 | 32 +- biogeochem/FatesCohortMod.F90 | 79 +- biogeochem/FatesPatchMod.F90 | 82 +- biogeophys/CMakeLists.txt | 1 + biogeophys/FatesLeafBiophysParamsMod.F90 | 28 +- biogeophys/FatesPlantHydraulicsMod.F90 | 19 +- biogeophys/FatesPlantRespPhotosynthMod.F90 | 37 +- biogeophys/LeafBiophysicsMod.F90 | 101 +- fire/FatesRxFireMod.F90 | 51 + fire/SFEquationsMod.F90 | 197 ++ fire/SFFireWeatherMod.F90 | 46 +- fire/SFMainMod.F90 | 454 ++-- fire/SFNesterovMod.F90 | 1 + fire/SFParamsMod.F90 | 114 + .../parteh/PartehDriver.py | 32 +- .../parteh/parteh_controls_phenevents_v2.xml | 4 +- .../parteh/parteh_controls_smoketests.xml | 4 +- .../parteh/parteh_controls_variable_netc.xml | 2 +- main/EDInitMod.F90 | 110 +- main/EDMainMod.F90 | 69 +- main/EDParamsMod.F90 | 112 +- main/EDPftvarcon.F90 | 11 +- main/EDTypesMod.F90 | 44 +- main/FatesConstantsMod.F90 | 46 +- main/FatesHistoryInterfaceMod.F90 | 1435 ++++++++----- main/FatesHistoryVariableType.F90 | 3 +- main/FatesInterfaceMod.F90 | 117 +- main/FatesInterfaceTypesMod.F90 | 52 +- main/FatesInventoryInitMod.F90 | 37 +- main/FatesRestartInterfaceMod.F90 | 353 +++- .../api39.0.0_050825_params_default.cdl | 1854 +++++++++++++++++ .../api40.0.0_060625_params_default.cdl | 1844 ++++++++++++++++ .../archive/api40.0.0_pr1355_patch_params.xml | 42 + .../archive/api40.0.0_pr1358_patch_params.xml | 28 + .../archive/api40.0.0_pr1359_patch_params.xml | 44 + .../archive/api41.0.0_pr1444_patch_params.xml | 119 ++ parameter_files/fates_params_default.cdl | 108 +- parteh/PRTAllometricCNPMod.F90 | 4 +- parteh/PRTAllometricCarbonMod.F90 | 7 +- parteh/PRTGenericMod.F90 | 31 + parteh/PRTLossFluxesMod.F90 | 3 +- parteh/PRTParametersMod.F90 | 27 +- parteh/PRTParamsFATESMod.F90 | 147 +- radiation/FatesRadiationDriveMod.F90 | 14 +- radiation/TwoStreamMLPEMod.F90 | 30 +- testing/CMakeLists.txt | 1 + testing/cime_setup.md | 3 - testing/functional_class_with_drivers.py | 15 + .../allometry/allometry_test.py | 3 +- .../functional_testing/fire/fuel/fuel_test.py | 5 +- .../fire/mortality/CMakeLists.txt | 24 + .../fire/mortality/FatesTestFireMortality.F90 | 456 ++++ .../fire/mortality/fire_mortality_test.py | 203 ++ .../functional_testing/fire/ros/ros_test.py | 2 +- .../fire/shr/SyntheticFuelModels.F90 | 2 +- .../math_utils/FatesTestMathUtils.F90 | 5 +- .../math_utils/math_utils_test.py | 2 +- .../functional_testing/patch/patch_test.py | 3 +- testing/functional_tests.cfg | 10 +- testing/load_functional_tests.py | 2 + testing/run_functional_tests.py | 65 +- testing/run_unit_tests.py | 15 +- testing/test_data/BONA_datm.nc | 3 + testing/testing_shr/FatesArgumentUtils.F90 | 2 +- testing/testing_shr/FatesFactoryMod.F90 | 53 +- testing/testing_shr/FatesUnitTestIOMod.F90 | 27 +- .../FatesUnitTestParamReaderMod.F90 | 2 +- testing/testing_shr/SyntheticPatchTypes.F90 | 6 +- .../fire_equations_test/test_FireEquations.pf | 271 +++ .../sort_cohorts_test/test_SortCohorts.pf | 2 +- testing/utils.py | 118 +- testing/utils_plotting.py | 101 + tools/UpdateParamAPI.py | 1 + 86 files changed, 8651 insertions(+), 2614 deletions(-) create mode 100644 fire/FatesRxFireMod.F90 create mode 100644 parameter_files/archive/api39.0.0_050825_params_default.cdl create mode 100644 parameter_files/archive/api40.0.0_060625_params_default.cdl create mode 100644 parameter_files/archive/api40.0.0_pr1355_patch_params.xml create mode 100644 parameter_files/archive/api40.0.0_pr1358_patch_params.xml create mode 100644 parameter_files/archive/api40.0.0_pr1359_patch_params.xml create mode 100644 parameter_files/archive/api41.0.0_pr1444_patch_params.xml create mode 100644 testing/functional_class_with_drivers.py create mode 100644 testing/functional_testing/fire/mortality/CMakeLists.txt create mode 100644 testing/functional_testing/fire/mortality/FatesTestFireMortality.F90 create mode 100644 testing/functional_testing/fire/mortality/fire_mortality_test.py create mode 100644 testing/test_data/BONA_datm.nc create mode 100644 testing/utils_plotting.py diff --git a/.gitattributes b/.gitattributes index e140598ee0..1c88af4852 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,16 +5,13 @@ # This is primarily being implemented to allow users to develop code with any operating system (OS) # preferred and mitigates potential problems with end of line (eol) character differences. # ----------------------------------------------------------------------------------------------------- - ## Set the default end of line behavior (i.e. normalization to `lf` upon commit) for all files git recognizes as text * text=auto - # Note that the above *only* applies to newly commited files. If the file previously existed with a `crlf` end of file # and was checked out to local, then git will not change the eol character during check-in (i.e. commit). For # windows users they will see a warning like this: # warning: CRLF will be replaced by LF in functional_unit_testing/allometry/drive_allomtests.py. # The file will have its original line endings in your working directory - ## Explicitly declare to git which files should be normalized (i.e. treated as text files) *.cdl text *.F90 text @@ -23,6 +20,6 @@ *.sh text *.txt text *.xml text - ## Declare to git which file types are binary files and should not have end of line modified -*.mod binary \ No newline at end of file +*.mod binary +testing/test_data/*.nc filter=lfs diff=lfs merge=lfs -text diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 87f6468203..f8424eb969 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -28,6 +28,9 @@ All checklist items must be checked to enable merging this pull request: *Integrator* - [ ] FATES PASS/FAIL regression tests were run - [ ] Evaluation of test results for answer changes was performed and results provided +- [ ] FATES-CLM6 Code Freeze: satellite phenology regression tests are b4b + +*If satellite phenology regressions are **not** b4b, please hold merge and notify the FATES development team.* ### Documentation diff --git a/.gitignore b/.gitignore index cf080967fe..a7413024b5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,9 @@ *.zip *.nc +# Allow netcdf files in the test data directory +!testing/test_data/*.nc + # Logs and databases # ###################### *.log @@ -54,4 +57,4 @@ _run/ # Old Files *~ # Editor specific setting files -*.vscode \ No newline at end of file +*.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ed5429a11..0883e7c5d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.4) list(APPEND CMAKE_MODULE_PATH ${CIME_CMAKE_MODULE_DIRECTORY}) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../share/cmake") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../components/cmeps/cmake") FIND_PATH(NETCDFC_FOUND libnetcdf.a ${NETCDF_C_DIR}/lib) FIND_PATH(NETCDFF_FOUND libnetcdff.a ${NETCDF_FORTRAN_DIR}/lib) @@ -19,6 +20,23 @@ include(CIME_utils) set(HLM_ROOT "../../") +if (DEFINED ENV{ESMF_ROOT}) + list(APPEND CMAKE_MODULE_PATH $ENV{ESMF_ROOT}/cmake) +endif() +find_package(ESMF REQUIRED) + +# This adds include directories needed for ESMF +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_F90COMPILEPATHS} ") +# This (which is *not* done in the share CMakeLists.txt) adds all directories and +# libraries needed when linking ESMF, including any dependencies of ESMF. (But note that +# this does *not* include the "-lesmf" itself). In particular, note that this includes any +# link flags needed to link against PIO, which is needed on some systems (including +# derecho); bringing in these PIO-related link flags via this ESMF mechanism allows us to +# avoid explicitly including PIO as a link library, which wouldn't work on systems where +# there is no separate PIO library and instead ESMF is built with its internal PIO +# library. +link_libraries(${ESMF_INTERFACE_LINK_LIBRARIES}) + # Add source directories from other share code (csm_share, etc.) add_subdirectory(${HLM_ROOT}/share/src csm_share) add_subdirectory(${HLM_ROOT}/share/unit_test_stubs/util csm_share_stubs) @@ -90,6 +108,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Directories and libraries to include in the link step link_directories(${CMAKE_CURRENT_BINARY_DIR}) +link_libraries(esmf) # Add the main test directory add_subdirectory(${HLM_ROOT}/src/fates/testing) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 45f5bce859..f41cc246f4 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -53,4 +53,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ -[wiki_ref_page]: https://github.com/NGEET/fates/wiki/Relevant-References +[references]: https://fates-users-guide.readthedocs.io/en/latest/user/Relevant-References.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b7aa26a795..532368d119 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,11 +12,11 @@ https://github.com/NGEET/fates/blob/master/CODE_OF_CONDUCT.md ## Getting Started -Those who wish to contribute code to FATES must have those changes integrated through the developer repository NGEET/fates. Changes that make it to public releases must go through this repository first, as well. Here are some basic first steps. +Those who wish to contribute code to FATES must have those changes integrated through the developer repository NGEET/fates. Changes that make it to public releases must go through this repository first, as well. Please refer to the [developer section](hhttps://fates-users-guide.readthedocs.io/en/latest/developer/developer-guide.html) of the [User's Guide](https://fates-users-guide.readthedocs.io/en/latest/index.html) for more details. Here are some basic first steps: * All developers should create a fork of the NGEET/fates repository into their personal space on github -* Follow the developer work-flow described here: https://github.com/NGEET/fates/wiki/FATES-Development-Workflow -* Each set of changes should have its own feature branch that encapsulates your desired changes, following the conventions outlined here: https://github.com/NGEET/fates/wiki/Feature-Branch-Naming-Convention +* Follow the [developer work-flow](https://fates-users-guide.readthedocs.io/en/latest/developer/FATES-Development-Workflow.html) +* Each set of changes should have its own [feature branch](https://fates-users-guide.readthedocs.io/en/latest/developer/Feature-Branch-Naming-Convention.html) that encapsulates your desired changes. * The work-flow will lead you eventually to submit a Pull-Request to NGEET/fates:master, please follow the template in the Pull Request and communicate as best you can if you are unsure how to fill out the text * It is best to create an issue to describe the work you are undertaking prior to starting. This helps the community sync with your efforts, prevents duplication of efforts, and science is not done in a vaccuum! * Expect peers to interact, help, discuss and eventually approve your submission (pull-request) @@ -31,11 +31,11 @@ In addition to the github discussions, we hold a roughly biweekly call, which co * Changes that are submitted should be limited to 1 single feature (i.e. don't submit changes to the radiation code and the nutrient cycle simultaneous, pick one thing) * Check for unnecessary whitespace with `git diff --check` before committing * We have no standard protocol for commit messages, but try to make them meaningful, concise and succinct. -* You will most likely have to test (see workflow above), see: https://github.com/NGEET/fates/wiki/Testing-Protocols +* You will most likely have to test (see workflow above), see the [testing protocols](https://fates-users-guide.readthedocs.io/en/latest/developer/Testing-Protocols.html) for more details. ## Coding Practices and Style -Please refer to the FATES style guide: https://github.com/NGEET/fates/wiki/Coding-Practices-and-Style-Guide +Please refer to the FATES [style guide](https://fates-users-guide.readthedocs.io/en/latest/developer/style.html) ## Trivial Changes diff --git a/README.md b/README.md index 3129042304..de06aa0e5b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ To receive email updates about forthcoming release tags, regular meeting notific [How to Contribute](https://github.com/NGEET/fates/blob/master/CONTRIBUTING.md) -[Table of FATES and Host Land Model API compatability](https://fates-users-guide.readthedocs.io/en/latest/user/Table-of-FATES-API-and-HLM-STATUS.html) +[Table of FATES and Host Land Model API compatability](https://fates-users-guide.readthedocs.io/en/latest/user/release-tags-compat-table.html) [List of Unsupported or Broken Features](https://fates-users-guide.readthedocs.io/en/latest/user/Current-Unsupported-or-Broken-Features.html) @@ -35,4 +35,4 @@ https://github.com/E3SM-Project/E3SM https://github.com/ESCOMP/cesm -The FATES, E3SM and CTSM teams maintain compatability of the NGEET/FATES master branch with the E3SM master and CTSM master branches respectively. There may be some modest lag time in which the latest commit on the FATES master branch is available to these host land models (HLM) by default. This is typically correlated with FATES development updates forcing necessary changes to the FATES API. See the table of [FATES API/HLM compatibility](https://fates-users-guide.readthedocs.io/en/latest/user/Table-of-FATES-API-and-HLM-STATUS.html) for information on which fates tag corresponds to which HLM tag or commit. +The FATES, E3SM and CTSM teams maintain compatability of the NGEET/FATES master branch with the E3SM master and CTSM master branches respectively. There may be some modest lag time in which the latest commit on the FATES master branch is available to these host land models (HLM) by default. This is typically correlated with FATES development updates forcing necessary changes to the FATES API. See the table of [FATES API/HLM compatibility](https://fates-users-guide.readthedocs.io/en/latest/user/release-tags-compat-table.html) for information on which fates tag corresponds to which HLM tag or commit. diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 081c734dd5..e21e1c56e7 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -25,8 +25,10 @@ module EDCanopyStructureMod use FatesAllometryMod , only : CrownDepth use FatesPatchMod, only : fates_patch_type use FatesCohortMod, only : fates_cohort_type - use EDParamsMod , only : nclmax - use EDParamsMod , only : nlevleaf + use EDParamsMod , only : nclmax + use EDParamsMod , only : nlevleaf + use EDParamsMod , only : GetNVegLayers + use EDParamsMod , only : comp_excln_exp use EDtypesMod , only : AREA use EDLoggingMortalityMod , only : UpdateHarvestC use FatesGlobals , only : endrun => fates_endrun @@ -72,8 +74,21 @@ module EDCanopyStructureMod integer :: istat ! return status code character(len=255) :: smsg ! Message string for deallocation errors + + ! Precision targets for demotion and promotion + ! We have two: + ! "pa_area_target_precision" is the required precision at the patch level, + ! we keep shuffling and splitting cohorts until each layer is within this precision + ! "co_area_target_precision" is the required precision at the cohort level, + ! essentially it is the minimum amount of change required to not ignore + ! a partial promotion or demotion + + real(r8), parameter :: pa_area_target_precision = 1.0E-11_r8 + real(r8), parameter :: co_area_target_precision = 1.0E-12_r8 + + integer, parameter :: demotion_phase = 1 + integer, parameter :: promotion_phase = 2 - real(r8), parameter :: area_target_precision = 1.0E-11_r8 ! Area conservation ! will attempt to reduce errors ! below this level @@ -86,9 +101,15 @@ module EDCanopyStructureMod ! can be roughly considered the same right? logical, parameter :: preserve_b4b = .true. - + + + ! If we want to allow some degree of imperfection + ! in canopy closure we would add it here + real(r8), parameter :: imperfect_fraction = 0._r8 + + ! 10/30/09: Created by Rosie Fisher - ! 2017/2018: Modifications and updates by Ryan Knox + ! 2017/2018/2025: Modifications and updates by Ryan Knox ! ============================================================================ contains @@ -118,7 +139,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! If we demote -all- the trees less than a given height, there is a massive advantage in being the cohort that is ! the biggest when the canopy is closed. ! In this implementation, the amount demoted, ('weight') is a function of the height weighted by the competitive exclusion - ! parameter (ED_val_comp_excln). + ! parameter (comp_excln_exp). ! Complexity in this routine results from a few things. ! Firstly, the complication of the demotion amount sometimes being larger than the cohort area (for a very small, short cohort) @@ -134,7 +155,6 @@ subroutine canopy_structure( currentSite , bc_in ) ! ! !USES: - use EDParamsMod, only : ED_val_comp_excln use EDTypesMod , only : min_patch_area ! @@ -149,9 +169,12 @@ subroutine canopy_structure( currentSite , bc_in ) integer :: i_lyr ! current layer index integer :: z ! Current number of canopy layers. (1= canopy, 2 = understorey) integer :: ipft - real(r8) :: arealayer(nclmax+2) ! Amount of plant area currently in each canopy layer + real(r8) :: arealayer(nclmax+5) ! Amount of plant area currently in each canopy layer integer :: patch_area_counter ! count iterations used to solve canopy areas logical :: area_not_balanced ! logical controlling if the patch layer areas + real(r8) :: target_area ! Canopy area that is either in excess/defiency + ! that is slated for demotion/promotion from/into layer + ! have successfully been redistributed integer :: return_code ! math checks on variables will return>0 if problems exist ! We only iterate because of possible imprecisions generated by the cohort @@ -177,6 +200,15 @@ subroutine canopy_structure( currentSite , bc_in ) ! do while (associated(currentPatch)) ! Patch loop + ! Make sure we are sorted + if(debug) call currentPatch%SortCohorts(check_order=.true.) + + ! Terminate cohorts before organizing canopy. That + ! step will be interested in preserving area, so termination + ! during that step will be counter productive + call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) + call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) + ! ------------------------------------------------------------------------------ ! Perform numerical checks on some cohort and patch structures ! ------------------------------------------------------------------------------ @@ -184,7 +216,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! canopy layer has a special bounds check currentCohort => currentPatch%tallest do while (associated(currentCohort)) - if( currentCohort%canopy_layer < 1 .or. currentCohort%canopy_layer > nclmax+1 ) then + if( currentCohort%canopy_layer < 1 ) then write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon write(fates_log(),*) 'BOGUS CANOPY LAYER: ',currentCohort%canopy_layer @@ -205,70 +237,63 @@ subroutine canopy_structure( currentSite , bc_in ) ! the layers below. ! --------------------------------------------------------------------------- - ! Its possible that before we even enter this scheme - ! some cohort numbers are very low. Terminate them. - call terminate_cohorts(currentSite, currentPatch, 1, 12, bc_in) - ! Calculate how many layers we have in this canopy ! This also checks the understory to see if its crown ! area is large enough to warrant a temporary sub-understory layer - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + z = NumCanopyLayers(currentPatch) do i_lyr = 1,z ! Loop around the currently occupied canopy layers. - call DemoteFromLayer(currentSite, currentPatch, i_lyr, bc_in) + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) + target_area = max(0._r8,arealayer(i_lyr) - (1._r8-imperfect_fraction)*currentPatch%area) + call PromoteOrDemote(currentSite, currentPatch, i_lyr, demotion_phase, target_area) end do - ! After demotions, we may then again have cohorts that are very very - ! very sparse, remove them - call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) - call fuse_cohorts(currentSite, currentPatch, bc_in) - ! Remove cohorts for various other reasons - call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) - - ! --------------------------------------------------------------------------------------- ! Promotion Phase: Identify if any upper-layers are underful and layers below them ! have cohorts that can be split and promoted to the layer above. ! --------------------------------------------------------------------------------------- - ! Re-calculate Number of layers without the false substory - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + ! Re-calculate Number of layers + z = NumCanopyLayers(currentPatch) ! We only promote if we have at least two layers if (z>1) then - - do i_lyr=1,z-1 - call PromoteIntoLayer(currentSite, currentPatch, i_lyr) + do i_lyr=2,z + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr-1,arealayer(i_lyr-1)) + target_area = max(0._r8,(1._r8-imperfect_fraction)*currentPatch%area - arealayer(i_lyr-1)) + call PromoteOrDemote(currentSite, currentPatch, i_lyr, promotion_phase, target_area) end do - ! Remove cohorts that are incredibly sparse - call terminate_cohorts(currentSite, currentPatch, 1,14,bc_in) - call fuse_cohorts(currentSite, currentPatch, bc_in) - ! Remove cohorts for various other reasons - call terminate_cohorts(currentSite, currentPatch, 2,14,bc_in) - end if ! --------------------------------------------------------------------------------------- ! Check on Layer Area (if the layer differences are not small ! Continue trying to demote/promote. Its possible on the first pass through, ! that cohort fusion has nudged the areas a little bit. + ! On all but the bottom layer, we expect the areas to match the area of the + ! patch with small precision, since we assume a PPA. On the lowest layer, + ! we only expect the area to be below the patch area. ! --------------------------------------------------------------------------------------- - z = NumPotentialCanopyLayers(currentPatch,currentSite%spread,include_substory=.false.) + z = NumCanopyLayers(currentPatch) area_not_balanced = .false. do i_lyr = 1,z call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) - if( ((arealayer(i_lyr)-currentPatch%area)/currentPatch%area > area_check_rel_precision) .or. & - ((arealayer(i_lyr)-currentPatch%area) > area_check_precision ) ) then - area_not_balanced = .true. - endif + if(i_lyr < z)then + if (abs(arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + area_not_balanced = .true. + end if + else + if ((arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + area_not_balanced = .true. + end if + end if enddo - + ! --------------------------------------------------------------------------------------- ! Gracefully exit if too many iterations have gone by ! --------------------------------------------------------------------------------------- @@ -277,40 +302,44 @@ subroutine canopy_structure( currentSite , bc_in ) if(patch_area_counter > max_patch_iterations .and. area_not_balanced) then write(fates_log(),*) 'PATCH AREA CHECK NOT CLOSING' write(fates_log(),*) 'patch area:',currentpatch%area - do i_lyr = 1,z - write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) - write(fates_log(),*) 'rel error: ',(arealayer(i_lyr)-currentPatch%area)/currentPatch%area - write(fates_log(),*) 'abs error: ',arealayer(i_lyr)-currentPatch%area - enddo + write(fates_log(),*) 'fraction that is imperfect (unclosed):',imperfect_fraction write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon write(fates_log(),*) 'spread:',currentSite%spread - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - write(fates_log(),*) 'coh ilayer:',currentCohort%canopy_layer - write(fates_log(),*) 'coh dbh:',currentCohort%dbh - write(fates_log(),*) 'coh pft:',currentCohort%pft - write(fates_log(),*) 'coh n:',currentCohort%n - write(fates_log(),*) 'coh carea:',currentCohort%c_area - ipft=currentCohort%pft - write(fates_log(),*) 'maxh:',prt_params%allom_dbh_maxheight(ipft) - write(fates_log(),*) 'lmode: ',prt_params%allom_lmode(ipft) - write(fates_log(),*) 'd2bl2: ',prt_params%allom_d2bl2(ipft) - write(fates_log(),*) 'd2bl_ediff: ',prt_params%allom_blca_expnt_diff(ipft) - write(fates_log(),*) 'd2ca_min: ',prt_params%allom_d2ca_coefficient_min(ipft) - write(fates_log(),*) 'd2ca_max: ',prt_params%allom_d2ca_coefficient_max(ipft) - currentCohort => currentCohort%shorter + do i_lyr = 1,z + write(fates_log(),*) '-----------------------------------------' + write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) + write(fates_log(),*) 'bias [m2] (layer-patch): ',(arealayer(i_lyr)- & + (1._r8-imperfect_fraction)*currentPatch%area) + currentCohort => currentPatch%tallest + do while (associated(currentCohort)) + if(currentCohort%canopy_layer == i_lyr)then + write(fates_log(),*) '-----------' + write(fates_log(),*) ' co area:',currentCohort%c_area + write(fates_log(),*) ' co dbh: ',currentCohort%dbh + write(fates_log(),*) ' co pft: ',currentCohort%pft + write(fates_log(),*) ' co n: ',currentCohort%n + end if + currentCohort => currentCohort%shorter + end do enddo + call endrun(msg=errMsg(sourcefile, __LINE__)) end if enddo ! do while(area_not_balanced) - ! Save number of canopy layers to the patch structure + ! Terminate any cohorts that are still outside the maximum number of + ! canopy layers. These terminations only occur in level 3 + call terminate_cohorts(currentSite, currentPatch, 3,17,bc_in) + + z = NumCanopyLayers(currentPatch) + ! Save number of canopy layers to the patch structure if(z > nclmax) then - write(fates_log(),*) 'Termination should have ensured number of canopy layers was not larger than nclmax' + write(fates_log(),*) 'Termination should have ensured number' + write(fates_log(),*) 'of canopy layers was not larger than nclmax' write(fates_log(),*) 'Predicted: ',z write(fates_log(),*) 'nclmax: ',nclmax write(fates_log(),*) 'Consider increasing nclmax if this value is to low' @@ -327,7 +356,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! neighbor is in level 2 set zstar as the ehight of that shortest level 1 cohort ! ------------------------------------------------------------------------------------------- - if ( ED_val_comp_excln .lt. 0.0_r8) then + if ( comp_excln_exp .lt. 0.0_r8) then currentPatch%zstar = 0._r8 currentCohort => currentPatch%tallest do while (associated(currentCohort)) @@ -348,905 +377,347 @@ subroutine canopy_structure( currentSite , bc_in ) return end subroutine canopy_structure - ! ============================================================================================== + subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) - subroutine DemoteFromLayer(currentSite,currentPatch,i_lyr,bc_in) - - use EDParamsMod, only : ED_val_comp_excln + ! -------------------------------------------------------------- + ! This routine will: + ! 1) Identify the list of cohorts that are in the appropriate + ! layer for promotion or demotion into the adjacent + ! layer + ! 2) Calculate the combined crown area of those cohorts + ! that will be transferred to the adjacent layer + ! 3) Perform the transfer either by re-assignment (if whole) + ! of by splitting the cohort + ! 4) Track the abundance and mass flows when promoting/demoting + ! -------------------------------------------------------------- - ! !ARGUMENTS - type(ed_site_type), intent(inout) :: currentSite - type(fates_patch_type), intent(inout) :: currentPatch - integer, intent(in) :: i_lyr ! Current canopy layer of interest - type(bc_in_type), intent(in) :: bc_in + ! Arguments + type(ed_site_type) :: site + type(fates_patch_type) :: patch + integer,intent(in) :: target_layer ! Canopy layer we draw from + integer,intent(in) :: phase ! promotion or demotion? + real(r8),intent(in) :: target_area ! Area we want to move [m2/ha] - ! !LOCAL VARIABLES: - type(fates_cohort_type), pointer :: currentCohort + ! Locals + type(fates_cohort_type), pointer :: cohort type(fates_cohort_type), pointer :: copyc - type(fates_cohort_type), pointer :: nextc ! The next cohort in line - integer :: i_cwd ! Index for CWD pool - real(r8) :: cc_loss ! cohort crown area loss in demotion (m2) - real(r8) :: leaf_c ! leaf carbon [kg] - real(r8) :: fnrt_c ! fineroot carbon [kg] - real(r8) :: sapw_c ! sapwood carbon [kg] - real(r8) :: store_c ! storage carbon [kg] - real(r8) :: struct_c ! structure carbon [kg] - real(r8) :: scale_factor ! for prob. exclusion - scales weight to a fraction - real(r8) :: scale_factor_min ! "" minimum before exeedance of 1 - real(r8) :: scale_factor_res ! "" applied to residual areas - real(r8) :: area_res ! residual area to demote after weakest cohort hits max - real(r8) :: newarea - real(r8) :: demote_area - real(r8) :: sumweights - real(r8) :: sumequal ! for rank-ordered same-size cohorts - ! this tallies their excluded area - real(r8) :: arealayer ! the area of the current canopy layer - logical :: tied_size_with_neighbors - real(r8) :: total_crownarea_of_tied_cohorts - - ! First, determine how much total canopy area we have in this layer - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) - - demote_area = arealayer - currentPatch%area - - if ( demote_area > area_target_precision ) then - - ! Is this layer currently over-occupied? - ! In that case, we need to work out which cohorts to demote. - ! We go in order from shortest to tallest for ranked demotion - - sumweights = 0.0_r8 - currentCohort => currentPatch%shortest - do while (associated(currentCohort)) - call carea_allom(currentCohort%dbh,currentCohort%n, & - currentSite%spread,currentCohort%pft, & - currentCohort%crowndamage, currentCohort%c_area) - - if(debug) then - if(currentCohort%c_area<0._r8)then - write(fates_log(),*) 'negative c_area stage 1d: ',currentCohort%dbh,i_lyr,currentCohort%n, & - currentSite%spread,currentCohort%pft,currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - if( currentCohort%canopy_layer == i_lyr)then - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - ! ---------------------------------------------------------- - ! Stochastic method. - ! Weight cohort demotion by inverse size to a constant power. - ! In this hypothesis, it is assumed that even the tallest - ! cohorts have a chance (although smaller) of being forced - ! to the understory. - ! ---------------------------------------------------------- - - currentCohort%excl_weight = 1._r8 / (currentCohort%height**ED_val_comp_excln) - sumweights = sumweights + currentCohort%excl_weight - - else - - ! ----------------------------------------------------------- - ! Rank ordered deterministic method - ! ----------------------------------------------------------- - ! If there are cohorts that have the exact same height (which is possible, really) - ! we don't want to unilaterally promote/demote one before the others. - ! So we <>mote them as a unit - ! now we need to go through and figure out how many equal-size cohorts there are. - ! then we need to go through, add up the collective crown areas of all equal-sized - ! and equal-canopy-layer cohorts, - ! and then demote from each as if they were a single group - - total_crownarea_of_tied_cohorts = currentCohort%c_area - - tied_size_with_neighbors = .false. - nextc => currentCohort%taller - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if( nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - tied_size_with_neighbors = .true. - total_crownarea_of_tied_cohorts = & - total_crownarea_of_tied_cohorts + nextc%c_area - end if - else - exit - endif - nextc => nextc%taller - end do - - if ( tied_size_with_neighbors ) then - - currentCohort%excl_weight = & - max(0.0_r8,min(currentCohort%c_area, & - (currentCohort%c_area/total_crownarea_of_tied_cohorts) * & - (demote_area - sumweights) )) - - sumequal = currentCohort%excl_weight - - nextc => currentCohort%taller - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if (nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - ! now we know the total crown area of all equal-sized, - ! equal-canopy-layer cohorts - nextc%excl_weight = & - max(0.0_r8,min(nextc%c_area, & - (nextc%c_area/total_crownarea_of_tied_cohorts) * & - (demote_area - sumweights) )) - sumequal = sumequal + nextc%excl_weight - end if - else - exit - endif - nextc => nextc%taller - end do - - ! Update the current cohort pointer to the last similar cohort - ! Its ok if this is not in the right layer - if(associated(nextc))then - currentCohort => nextc%shorter - else - currentCohort => currentPatch%tallest - end if - sumweights = sumweights + sumequal - - else - currentCohort%excl_weight = & - max(min(currentCohort%c_area, demote_area - sumweights ), 0._r8) - sumweights = sumweights + currentCohort%excl_weight - end if - - endif - endif - currentCohort => currentCohort%taller - enddo - - ! If this is probabalistic demotion, we need to do a round of normalization. - ! And then a few rounds where we pre-calculate the demotion areas - ! and adjust things if the demoted area wants to be greater than - ! what is available. The math is too hard to explain here, see - ! the tech note section on promotion/demotion. - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - scale_factor_min = 1.e10_r8 - scale_factor = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - if(currentCohort%canopy_layer == i_lyr) then - currentCohort%excl_weight = currentCohort%excl_weight/sumweights - if( 1._r8/currentCohort%excl_weight < scale_factor_min ) & - scale_factor_min = 1._r8/currentCohort%excl_weight - - scale_factor = scale_factor + currentCohort%excl_weight * currentCohort%c_area - - endif - currentCohort => currentCohort%shorter - enddo - - ! This is the factor by which we need to multiply - ! the demotion probabilities, so the sum result equals - ! the total amount to demote - - scale_factor = demote_area/scale_factor - - if(scale_factor <= scale_factor_min) then - - ! Trivial case, all of the demotion fractions are less than 1. - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - currentCohort%excl_weight = currentCohort%c_area * currentCohort%excl_weight * scale_factor - - if(debug) then - if((currentCohort%excl_weight > (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%excl_weight < 0._r8) ) then - write(fates_log(),*) 'exclusion area too big (1)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'dbh: ',currentCohort%dbh - write(fates_log(),*) 'n: ',currentCohort%n - write(fates_log(),*) 'spread: ',currentSite%spread - write(fates_log(),*) 'pft: ',currentCohort%pft - write(fates_log(),*) 'currentCohort%excl_weight: ',currentCohort%excl_weight - write(fates_log(),*) 'excess: ',currentCohort%excl_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - else - - - ! Non-trivial case, at least 1 cohort's demotion - ! rate would exceed its area, given the trivial scale factor - - area_res = 0._r8 - scale_factor_res = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - area_res = area_res + & - currentCohort%c_area * currentCohort%excl_weight * & - scale_factor_min - scale_factor_res = scale_factor_res + & - currentCohort%c_area * & - (1._r8 - (currentCohort%excl_weight * scale_factor_min)) - endif - currentCohort => currentCohort%shorter - enddo - - area_res = demote_area - area_res - - scale_factor_res = area_res / scale_factor_res - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - - currentCohort%excl_weight = currentCohort%c_area * & - (currentCohort%excl_weight * scale_factor_min + & - (1._r8 - (currentCohort%excl_weight*scale_factor_min) ) * scale_factor_res) - - if(debug)then - if((currentCohort%excl_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%excl_weight < 0._r8) ) then - write(fates_log(),*) 'exclusion area error (2)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%excl_weight: ', & - currentCohort%excl_weight - write(fates_log(),*) 'excess: ', & - currentCohort%excl_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - end if - - end if - - - ! perform a check and see if the demotions meet the demand - sumweights = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == i_lyr) then - sumweights = sumweights + currentCohort%excl_weight - end if - currentCohort => currentCohort%shorter - end do - - if (abs(sumweights - demote_area) > area_check_precision ) then - write(fates_log(),*) 'demotions dont add up' - write(fates_log(),*) 'sum demotions: ',sumweights - write(fates_log(),*) 'area needed to be demoted: ',demote_area - write(fates_log(),*) 'excess: ',sumweights - demote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - - ! Weights have been calculated. Now move them to the lower layer - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - nextc => currentCohort%shorter - - if(currentCohort%canopy_layer == i_lyr )then - - cc_loss = currentCohort%excl_weight - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - if ( (cc_loss-currentCohort%c_area) > -nearzero .and. & - (cc_loss-currentCohort%c_area) < area_target_precision ) then - - ! If the whole cohort is being demoted, just change its - ! layer index - - currentCohort%canopy_layer = i_lyr+1 - - ! keep track of number and biomass of demoted cohort - currentSite%demotion_rate(currentCohort%size_class) = & - currentSite%demotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%demotion_carbonflux = currentSite%demotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * currentCohort%n - - elseif( (cc_loss < currentCohort%c_area) .and. & - (cc_loss > area_target_precision) ) then - - ! If only part of the cohort is demoted - ! then it must be split (little more complicated) - - ! Make a copy of the current cohort. The copy and the original - ! conserve total number density of the original. The copy - ! remains in the upper-story. The original is the one - ! demoted to the understory - - - allocate(copyc) - - ! (keep as an example) - ! Initialize running means - !allocate(copyc%tveg_lpa) - !!allocate(copyc%l2fr_ema) - ! Note, no need to give a starter value here, - ! that will be taken care of in copy() - !!call copyc%l2fr_ema%InitRMean(ema_60day) - - ! Initialize the PARTEH object and point to the - ! correct boundary condition fields - copyc%prt => null() - call InitPRTObject(copyc%prt) - - if( hlm_use_planthydro.eq.itrue ) then - call InitHydrCohort(currentSite,copyc) - endif - - call currentCohort%Copy(copyc) - call copyc%InitPRTBoundaryConditions() - - newarea = currentCohort%c_area - cc_loss - copyc%n = currentCohort%n*newarea/currentCohort%c_area - currentCohort%n = currentCohort%n - copyc%n - - copyc%canopy_layer = i_lyr !the taller cohort is the copy - - ! Demote the current cohort to the understory. - currentCohort%canopy_layer = i_lyr + 1 - - ! keep track of number and biomass of demoted cohort - currentSite%demotion_rate(currentCohort%size_class) = & - currentSite%demotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%demotion_carbonflux = currentSite%demotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * currentCohort%n - - call carea_allom(copyc%dbh,copyc%n,currentSite%spread,copyc%pft, & - copyc%crowndamage, copyc%c_area) - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - - !----------- Insert copy into linked list ------------------------! - copyc%shorter => currentCohort - if(associated(currentCohort%taller))then - copyc%taller => currentCohort%taller - currentCohort%taller%shorter => copyc - else - currentPatch%tallest => copyc - copyc%taller => null() - endif - currentCohort%taller => copyc - - elseif(cc_loss > currentCohort%c_area)then - - write(fates_log(),*) 'more area than the cohort has is being demoted' - write(fates_log(),*) 'loss:',cc_loss - write(fates_log(),*) 'existing area:',currentCohort%c_area - write(fates_log(),*) 'excess: ',cc_loss - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - - end if - - ! kill the ones which go into canopy layers that are not allowed - ! USE THIS OVERRIDE IF YOU ARE FORCING A ONE COHORT SIMULATION - ! (also make sure to turn off germination, external seed rain, - ! (use only one PFT, and make sure disturb_frac is 0) - ! (RGK-0822) - !if(currentCohort%canopy_layer>1) then - - if(currentCohort%canopy_layer>nclmax )then - ! put the litter from the terminated cohorts - ! straight into the fragmenting pools - call terminate_cohort(currentSite,currentPatch,currentCohort,bc_in,i_term_mort_type_canlev) - deallocate(currentCohort, stat=istat, errmsg=smsg) - if (istat/=0) then - write(fates_log(),*) 'dealloc012: fail on deallocate(currentCohort):'//trim(smsg) - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif - else - call carea_allom(currentCohort%dbh,currentCohort%n, & - currentSite%spread,currentCohort%pft,currentCohort%crowndamage, & - currentCohort%c_area) - end if + real(r8) :: promdem_area ! Actual area promoted or demoted (minimum of target + ! and existing canopy area) + real(r8) :: sumpd_area ! Sum crown area of all cohorts in layer [m2/ha] + real(r8) :: group_area ! Sum area of cohorts with the same height [m2/ha] + real(r8) :: remainder_area ! The area that has not been accounted + real(r8) :: excess_area ! The area that could not be accounted + real(r8) :: attempt_area ! Amount of area attempted to donate probabilistically + real(r8) :: max_donate_area ! This is the total area of the layer + ! for as seeks to fill out the target_area + real(r8) :: leaf_c, store_c + real(r8) :: fnrt_c, sapw_c + real(r8) :: struct_c + integer :: ilyr_change ! layer offset from current for the destination (+/- 1) + integer :: ic,ic_n,ic_nn ! Cohort indices + integer :: n_layer ! The number of cohorts in the layer + + + if (target_area patch%co_scr) + + ! Step 1: Determine which cohorts are in the layer + ! and point to them in the scratch vector + ! Make sure their areas are updated too. + ! We point to them in the scratch + ! vector in order of promotion/demotion, + ! note that this is inconsequential for probabalistic + + ic = 0 + group_area = 0._r8 + if(phase==demotion_phase) then + cohort => patch%shortest + ilyr_change = 1 + else + cohort => patch%tallest + ilyr_change = -1 + end if + do while (associated(cohort)) + if(cohort%canopy_layer == target_layer)then + ic = ic + 1 + call carea_allom(cohort%dbh,cohort%n,site%spread, & + cohort%pft,cohort%crowndamage,cohort%c_area) + group_area = group_area + cohort%c_area + layer_co(ic)%p => cohort + end if + if(phase==demotion_phase) then + cohort => cohort%taller + else + cohort => cohort%shorter + end if + end do - endif !canopy layer = i_ly + ! We update the target area to be no more than the + ! area of the layer (can't take more than there is..) + promdem_area = min(target_area,group_area) - ! We dont use our typical (point to smaller) - ! here, because, we may had deallocated the existing - ! currentCohort + ! Store the number of cohorts in the layer + ! and zero out the array of area transfers + n_layer = ic - currentCohort => nextc - enddo !currentCohort + do ic = 1,n_layer + layer_co(ic)%pd_area = 0._r8 + end do + ! Step 2: Calculate the promotion or demotion areas + comp_excl_type: if (comp_excln_exp .ge. 0.0_r8 ) then - ! Update the area calculations of the current layer - ! And the layer below that may or may not had recieved - ! Demotions + ! ------------------------------------------------------------------ + ! Stochastic case + ! ------------------------------------------------------------------ - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) + if_not_trivial: if(target_area >= group_area)then - if ( (abs(arealayer - currentPatch%area)/arealayer > area_check_rel_precision ) .or. & - (abs(arealayer - currentPatch%area) > area_check_precision) ) then - write(fates_log(),*) 'demotion did not trim area within tolerance' - write(fates_log(),*) 'arealayer:',arealayer - write(fates_log(),*) 'patch%area:',currentPatch%area - write(fates_log(),*) 'ilayer: ',i_lyr - write(fates_log(),*) 'bias:',arealayer - currentPatch%area - write(fates_log(),*) 'rel bias:',(arealayer - currentPatch%area)/arealayer - write(fates_log(),*) 'demote_area:',demote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + ! If promotion/demotion is so large that it + ! is larger than available area in the layer, + ! the trivial solution is to just set the + ! promotion/demotion areas to the cohort areas + ! in the layer and move on. (We don't need to + ! do this for rank-ordered, that algorithm + ! handles this just fine and would just make + ! it less readable)" + + do ic = 1,n_layer + layer_co(ic)%pd_area = layer_co(ic)%p%c_area + end do + + else + + sumpd_area = 0._r8 + do ic = 1,n_layer + cohort => layer_co(ic)%p + if(phase==demotion_phase) then + layer_co(ic)%pd_area = cohort%c_area/(cohort%height**comp_excln_exp) + elseif(phase==promotion_phase) then + layer_co(ic)%pd_area = cohort%c_area*cohort%height**comp_excln_exp + end if + sumpd_area = sumpd_area + layer_co(ic)%pd_area + end do + + ! Distribute areas in a first pass + ! For those cohorts where more area was to be donated + ! than it has, accumulate the excess. For those + ! cohorts that are not filled and still have area to + ! donate, accumulate remainder. We will use these in + ! the next step to portion out area. + + excess_area = 0._r8 + remainder_area = 0._r8 + do ic = 1,n_layer + cohort => layer_co(ic)%p + attempt_area = promdem_area*layer_co(ic)%pd_area/sumpd_area + if(attempt_area>cohort%c_area)then + excess_area = excess_area + (attempt_area - cohort%c_area) + else + remainder_area = remainder_area + (cohort%c_area - attempt_area) + end if + layer_co(ic)%pd_area = min(cohort%c_area,attempt_area) + end do + + if(excess_area>nearzero)then + + ! The "if_not_trivial" condition above should prevent + ! a situation at this point in the code where all + ! promotion/demotions are larger than the layer area + ! and thus remainder area is zero. + if(remainder_area<=0._r8)then + write(fates_log(),*) 'prob. prom/dem has encountered a situation' + write(fates_log(),*) 'where it thinks all weightings are greater' + write(fates_log(),*) 'than the layer areas, but somehow' + write(fates_log(),*) 'was not captured at the beginning of the' + write(fates_log(),*) 'routine in the if_not_trivial clause.' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + + do ic = 1,n_layer + cohort => layer_co(ic)%p + ! look at just the cohorts that still have space to give + ! remove from them the same fraction of their remaining space + if (abs(layer_co(ic)%pd_area-cohort%c_area) > nearzero) then + layer_co(ic)%pd_area = layer_co(ic)%pd_area + & + (excess_area/remainder_area) * & + (cohort%c_area - layer_co(ic)%pd_area) + end if + end do + end if + end if if_not_trivial + + else !comp_excl_exp < 0 + + ! ------------------------------------------------------------------ + ! Rank Ordered Case + ! ------------------------------------------------------------------ + + sumpd_area = 0._r8 + ic = 1 + do while( ic<=n_layer .and. (promdem_area-sumpd_area)>co_area_target_precision) + + cohort => layer_co(ic)%p + + ! Determine if the next cohorts in + ! order have the same height + + group_area = cohort%c_area + ic_n = ic + check_next:do while(ic_n similar_height_tol ) then + exit check_next + else + ic_n = ic_n + 1 + group_area = group_area+layer_co(ic_n)%p%c_area + end if + end do check_next + + remainder_area = min(promdem_area-sumpd_area,group_area) + do ic_nn = ic,ic_n + layer_co(ic_nn)%pd_area = remainder_area*layer_co(ic_nn)%p%c_area/group_area + sumpd_area = sumpd_area + layer_co(ic_nn)%pd_area + end do + + ic = ic_n + 1 + + end do + end if comp_excl_type + + ! Check to make sure the changes are within bounds + do ic = 1,n_layer + cohort => layer_co(ic)%p + if( ((layer_co(ic)%pd_area - cohort%c_area) > co_area_target_precision ) .or. & + (layer_co(ic)%pd_area < 0._r8) ) then + write(fates_log(),*) 'negative,or more area than the cohort has is being promoted/demoted' + write(fates_log(),*) 'change: ',layer_co(ic)%pd_area + write(fates_log(),*) 'existing area:',cohort%c_area + write(fates_log(),*) 'excess: ',layer_co(ic)%pd_area - cohort%c_area + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end do + + ! Part 3: + ! Apply the area changes by splitting the cohort and re-assigning + ! either all or part of it to a new layer + + ic_loop0: do ic = 1,n_layer + + cohort => layer_co(ic)%p + + ! If the dem/prom area is the same area as the + ! cohort itself, move the whole thing + ! If the dem/prom area is less than the cohort area + ! and not trivialy small (larger than precision + ! check), then split it and move part of it + ! If the dem/prom area is less than zero or larger than + ! the cohort area within precision checks then + ! we would have failed in the previous checks + + whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < & + co_area_target_precision ) then + + ! Whole cohort promotion/demotion + cohort%canopy_layer = cohort%canopy_layer + ilyr_change + + elseif( (layer_co(ic)%pd_area < cohort%c_area) .and. & + (layer_co(ic)%pd_area > 0 ) ) then + + ! Partial cohort promotion/demotion + + ! Make a copy of the current cohort. The copy and the original + ! conserve total number density. The copy + ! remains in the upper-story. The original is the one + ! demoted to the understory + + + allocate(copyc) + + ! (keep as an example) + ! Initialize running means + !allocate(copyc%tveg_lpa) + !!allocate(copyc%l2fr_ema) + ! Note, no need to give a starter value here, + ! that will be taken care of in copy() + !!call copyc%l2fr_ema%InitRMean(ema_60day) + + ! Initialize the PARTEH object and point to the + ! correct boundary condition fields + copyc%prt => null() + call InitPRTObject(copyc%prt) + + if( hlm_use_planthydro.eq.itrue ) then + call InitHydrCohort(site,copyc) + endif + + call cohort%Copy(copyc) + call copyc%InitPRTBoundaryConditions() + + remainder_area = cohort%c_area - layer_co(ic)%pd_area + copyc%n = cohort%n*remainder_area/cohort%c_area + cohort%n = cohort%n - copyc%n + + ! The copied cohort is the part that remains in-layer + copyc%canopy_layer = cohort%canopy_layer + + ! The original cohort changes layers + cohort%canopy_layer = cohort%canopy_layer + ilyr_change + + call carea_allom(copyc%dbh,copyc%n,site%spread,copyc%pft, & + copyc%crowndamage, copyc%c_area) + call carea_allom(cohort%dbh,cohort%n,site%spread, & + cohort%pft, cohort%crowndamage, cohort%c_area) + + !----------- Insert copy into linked list ------------------------! + ! Since we are not changing the heights, no sorting necessary + !-----------------------------------------------------------------! + copyc%shorter => cohort + if(associated(cohort%taller))then + copyc%taller => cohort%taller + cohort%taller%shorter => copyc + else + patch%tallest => copyc + copyc%taller => null() + endif + cohort%taller => copyc + + end if whole_or_part + + ! Part 4: + ! keep track of number and biomass promoted/demoted + + leaf_c = cohort%prt%GetState(leaf_organ,carbon12_element) + store_c = cohort%prt%GetState(store_organ,carbon12_element) + fnrt_c = cohort%prt%GetState(fnrt_organ,carbon12_element) + sapw_c = cohort%prt%GetState(sapw_organ,carbon12_element) + struct_c = cohort%prt%GetState(struct_organ,carbon12_element) + + if(phase==demotion_phase) then + site%demotion_rate(cohort%size_class) = & + site%demotion_rate(cohort%size_class) + cohort%n + site%demotion_carbonflux = site%demotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + else + site%promotion_rate(cohort%size_class) = & + site%promotion_rate(cohort%size_class) + cohort%n + site%promotion_carbonflux = site%promotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + end if + end do ic_loop0 - end if + end associate - return - end subroutine DemoteFromLayer + end subroutine PromoteOrDemote ! ============================================================================================== - - subroutine PromoteIntoLayer(currentSite,currentPatch,i_lyr) - - ! ------------------------------------------------------------------------------------------- - ! Check whether the intended 'full' layers are actually filling all the space. - ! If not, promote some fraction of cohorts upwards. - ! THIS SECTION MIGHT BE TRIGGERED BY A FIRE OR MORTALITY EVENT, FOLLOWED BY A PATCH FUSION, - ! SO THE TOP LAYER IS NO LONGER FULL. - ! ------------------------------------------------------------------------------------------- - - use EDParamsMod, only : ED_val_comp_excln - - ! !ARGUMENTS - type(ed_site_type), intent(inout), target :: currentSite - type(fates_patch_type), intent(inout), target :: currentPatch - integer, intent(in) :: i_lyr ! Current canopy layer of interest - - ! !LOCAL VARIABLES: - type(fates_cohort_type), pointer :: currentCohort - type(fates_cohort_type), pointer :: copyc - type(fates_cohort_type), pointer :: nextc ! the next cohort, or used for looping - ! cohorts against the current - - real(r8) :: scale_factor ! for prob. exclusion - scales weight to a fraction - real(r8) :: scale_factor_min ! "" minimum before exeedance of 1 - real(r8) :: scale_factor_res ! "" applied to residual areas - real(r8) :: area_res ! residual area to demote after weakest cohort hits max - real(r8) :: promote_area - real(r8) :: newarea - real(r8) :: sumweights - real(r8) :: sumequal ! for tied cohorts, the sum of weights in - ! their group - real(r8) :: cc_gain ! cohort crown area gain in promotion (m2) - real(r8) :: arealayer_current ! area (m2) of the current canopy layer - real(r8) :: arealayer_below ! area (m2) of the layer below the current layer - real(r8) :: leaf_c ! leaf carbon [kg] - real(r8) :: fnrt_c ! fineroot carbon [kg] - real(r8) :: sapw_c ! sapwood carbon [kg] - real(r8) :: store_c ! storage carbon [kg] - real(r8) :: struct_c ! structure carbon [kg] - - logical :: tied_size_with_neighbors - real(r8) :: total_crownarea_of_tied_cohorts - - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer_current) - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr+1,arealayer_below) - - - ! how much do we need to gain? - promote_area = currentPatch%area - arealayer_current - - if( promote_area > area_target_precision ) then - - if(arealayer_below <= promote_area ) then - - ! --------------------------------------------------------------------------- - ! Promote all cohorts from layer below if that whole layer has area smaller - ! than the tolerance on the gains needed into current layer - ! --------------------------------------------------------------------------- - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - !look at the cohorts in the canopy layer below... - if(currentCohort%canopy_layer == i_lyr+1)then - - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - currentCohort%canopy_layer = i_lyr - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(currentCohort%size_class) = & - currentSite%promotion_rate(currentCohort%size_class) + currentCohort%n - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * currentCohort%n - - endif - currentCohort => currentCohort%shorter - enddo - - else - - ! --------------------------------------------------------------------------- - ! This is the non-trivial case where the lower layer can accomodate - ! more than what is necessary. - ! --------------------------------------------------------------------------- - - - ! figure out with what weighting we need to promote cohorts. - ! This is the opposite of the demotion weighting... - - sumweights = 0.0_r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage,currentCohort%c_area) - if(currentCohort%canopy_layer == i_lyr+1)then !look at the cohorts in the canopy layer below... - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - ! ------------------------------------------------------------------ - ! Stochastic case, as above (in demotion portion of code) - ! ------------------------------------------------------------------ - - currentCohort%prom_weight = currentCohort%height**ED_val_comp_excln - sumweights = sumweights + currentCohort%prom_weight - else - - ! ------------------------------------------------------------------ - ! Rank ordered deterministic method - ! If there are cohorts that have the exact same height (which is possible, really) - ! we don't want to unilaterally promote/demote one before the others. - ! So we <>mote them as a unit - ! now we need to go through and figure out how many equal-size cohorts there are. - ! then we need to go through, add up the collective crown areas of all equal-sized - ! and equal-canopy-layer cohorts, - ! and then demote from each as if they were a single group - ! ------------------------------------------------------------------ - - total_crownarea_of_tied_cohorts = currentCohort%c_area - tied_size_with_neighbors = .false. - nextc => currentCohort%shorter - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if( nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - tied_size_with_neighbors = .true. - total_crownarea_of_tied_cohorts = & - total_crownarea_of_tied_cohorts + nextc%c_area - end if - else - exit - endif - nextc => nextc%shorter - end do - - if ( tied_size_with_neighbors ) then - - currentCohort%prom_weight = & - max(0.0_r8,min(currentCohort%c_area, & - (currentCohort%c_area/total_crownarea_of_tied_cohorts) * & - (promote_area - sumweights) )) - sumequal = currentCohort%prom_weight - - nextc => currentCohort%shorter - do while (associated(nextc)) - if ( abs(nextc%height - currentCohort%height) < similar_height_tol ) then - if (nextc%canopy_layer .eq. currentCohort%canopy_layer ) then - ! now we know the total crown area of all equal-sized, - ! equal-canopy-layer cohorts - nextc%prom_weight = & - max(0.0_r8,min(nextc%c_area, & - (nextc%c_area/total_crownarea_of_tied_cohorts) * & - (promote_area - sumweights) )) - sumequal = sumequal + nextc%prom_weight - end if - else - exit - endif - nextc => nextc%shorter - end do - - ! Update the current cohort pointer to the last similar cohort - ! Its ok if this is not in the right layer - if(associated(nextc))then - currentCohort => nextc%taller - else - currentCohort => currentPatch%shortest - end if - sumweights = sumweights + sumequal - - else - currentCohort%prom_weight = & - max(min(currentCohort%c_area, promote_area - sumweights ), 0._r8) - sumweights = sumweights + currentCohort%prom_weight - - end if - - endif - endif - currentCohort => currentCohort%shorter - enddo !currentCohort - - - ! If this is probabalistic promotion, we need to do a round of normalization. - ! And then a few rounds where we pre-calculate the promotion areas - ! and adjust things if the promoted area wants to be greater than - ! what is available. - - if (ED_val_comp_excln .ge. 0.0_r8 ) then - - scale_factor_min = 1.e10_r8 - scale_factor = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - if(currentCohort%canopy_layer == (i_lyr+1) ) then - - currentCohort%prom_weight = currentCohort%prom_weight/sumweights - if( 1._r8/currentCohort%prom_weight < scale_factor_min ) & - scale_factor_min = 1._r8/currentCohort%prom_weight - - scale_factor = scale_factor + currentCohort%prom_weight * currentCohort%c_area - - endif - currentCohort => currentCohort%shorter - enddo - - ! This is the factor by which we need to multiply - ! the demotion probabilities, so the sum result equals - ! the total amount to demote - scale_factor = promote_area/scale_factor - - - if(scale_factor <= scale_factor_min) then - - ! Trivial case, all of the demotion fractions - ! are less than 1. - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1) ) then - currentCohort%prom_weight = currentCohort%c_area * & - currentCohort%prom_weight * scale_factor - - if(debug)then - if((currentCohort%prom_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%prom_weight < 0._r8) ) then - write(fates_log(),*) 'promotion area too big (1)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%prom_weight: ', & - currentCohort%prom_weight - write(fates_log(),*) 'excess: ', & - currentCohort%prom_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - else - - ! Non-trivial case, at least 1 cohort's promotion - ! rate would exceed its area, given the trivial scale factor - - area_res = 0._r8 - scale_factor_res = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1) ) then - area_res = area_res + & - currentCohort%c_area*currentCohort%prom_weight*scale_factor_min - scale_factor_res = scale_factor_res + & - currentCohort%c_area * & - (1._r8 - (currentCohort%prom_weight * scale_factor_min)) - endif - currentCohort => currentCohort%shorter - enddo - - area_res = promote_area - area_res - - scale_factor_res = area_res / scale_factor_res - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1)) then - - currentCohort%prom_weight = currentCohort%c_area * & - (currentCohort%prom_weight * scale_factor_min + & - (1._r8 - (currentCohort%prom_weight*scale_factor_min) ) * & - scale_factor_res) - - if(debug)then - if((currentCohort%prom_weight > & - (currentCohort%c_area+area_target_precision)) .or. & - (currentCohort%prom_weight < 0._r8) ) then - write(fates_log(),*) 'promotion area error (2)' - write(fates_log(),*) 'currentCohort%c_area: ',currentCohort%c_area - write(fates_log(),*) 'currentCohort%prom_weight: ', & - currentCohort%prom_weight - write(fates_log(),*) 'excess: ', & - currentCohort%prom_weight - currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - endif - currentCohort => currentCohort%shorter - enddo - - end if - - end if - - - ! lets perform a check and see if the promotions meet the demand - sumweights = 0._r8 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == (i_lyr+1)) then - sumweights = sumweights + currentCohort%prom_weight - end if - currentCohort => currentCohort%shorter - end do - - if(debug)then - if (abs(sumweights - promote_area) > area_check_precision ) then - write(fates_log(),*) 'promotions dont add up' - write(fates_log(),*) 'sum promotions: ',sumweights - write(fates_log(),*) 'area needed to be promoted: ',promote_area - write(fates_log(),*) 'excess: ',sumweights - promote_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if - - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - - - !All the trees in this layer need to promote some area upwards... - if( (currentCohort%canopy_layer == i_lyr+1) ) then - - cc_gain = currentCohort%prom_weight - leaf_c = currentCohort%prt%GetState(leaf_organ,carbon12_element) - store_c = currentCohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = currentCohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ,carbon12_element) - - if ( (cc_gain-currentCohort%c_area) > -nearzero .and. & - (cc_gain-currentCohort%c_area) < area_target_precision ) then - - currentCohort%canopy_layer = i_lyr - - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(currentCohort%size_class) = & - currentSite%promotion_rate(currentCohort%size_class) + currentCohort%n - - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * currentCohort%n - - elseif ( (cc_gain < currentCohort%c_area) .and. & - (cc_gain > area_target_precision) ) then - - allocate(copyc) - - - !!allocate(copyc%l2fr_ema) - ! Note, no need to give a starter value here, - ! that will be taken care of in copy() - !!call copyc%l2fr_ema%InitRMean(ema_60day) - - ! Initialize the PARTEH object and point to the - ! correct boundary condition fields - copyc%prt => null() - call InitPRTObject(copyc%prt) - - - if( hlm_use_planthydro.eq.itrue ) then - call InitHydrCohort(CurrentSite,copyc) - endif - - ! (keep as an example) - ! Initialize running means - !allocate(copyc%tveg_lpa) - !call copyc%tveg_lpa%InitRMean(ema_lpa,& - ! init_value=currentPatch%tveg_lpa%GetMean()) - - call currentCohort%Copy(copyc) !makes an identical copy... - call copyc%InitPRTBoundaryConditions() - - newarea = currentCohort%c_area - cc_gain !new area of existing cohort - - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - - ! number of individuals in promoted cohort. - copyc%n = currentCohort%n*cc_gain/currentCohort%c_area - - ! number of individuals in cohort remaining in understorey - currentCohort%n = currentCohort%n - copyc%n - - currentCohort%canopy_layer = i_lyr + 1 ! keep current cohort in the understory. - copyc%canopy_layer = i_lyr ! promote copy to the higher canopy layer. - - ! keep track of number and biomass of promoted cohort - currentSite%promotion_rate(copyc%size_class) = & - currentSite%promotion_rate(copyc%size_class) + copyc%n - - currentSite%promotion_carbonflux = currentSite%promotion_carbonflux + & - (leaf_c + fnrt_c + store_c + sapw_c + struct_c) * copyc%n - - call carea_allom(currentCohort%dbh,currentCohort%n,currentSite%spread, & - currentCohort%pft,currentCohort%crowndamage, currentCohort%c_area) - call carea_allom(copyc%dbh,copyc%n,currentSite%spread,copyc%pft,& - copyc%crowndamage,copyc%c_area) - - !----------- Insert copy into linked list ------------------------! - copyc%shorter => currentCohort - if(associated(currentCohort%taller))then - copyc%taller => currentCohort%taller - currentCohort%taller%shorter => copyc - else - currentPatch%tallest => copyc - copyc%taller => null() - endif - currentCohort%taller => copyc - - elseif(cc_gain > currentCohort%c_area)then - - write(fates_log(),*) 'more area than the cohort has is being promoted' - write(fates_log(),*) 'loss:',cc_gain - write(fates_log(),*) 'existing area:',currentCohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - - endif - - endif ! if(currentCohort%canopy_layer == i_lyr+1) then - currentCohort => currentCohort%shorter - enddo !currentCohort - - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer_current) - - if ((abs(arealayer_current - currentPatch%area)/arealayer_current > & - area_check_rel_precision ) .or. & - (abs(arealayer_current - currentPatch%area) > area_check_precision) ) then - write(fates_log(),*) 'promotion did not bring area within tolerance' - write(fates_log(),*) 'arealayer:',arealayer_current - write(fates_log(),*) 'patch%area:',currentPatch%area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - end if - - end if - - return - end subroutine PromoteIntoLayer - - ! ============================================================================ - + subroutine canopy_spread( currentSite ) ! ! !DESCRIPTION: @@ -1406,9 +877,9 @@ subroutine canopy_summarization( nsites, sites, bc_in ) call endrun(msg=errMsg(sourcefile, __LINE__)) end if - if (currentPatch%total_canopy_area - currentPatch%area > area_error_1) then + if (currentPatch%total_canopy_area - (1._r8-imperfect_fraction)*currentPatch%area > area_error_1) then write(fates_log(),*) 'too much canopy in summary', s, & - currentPatch%nocomp_pft_label, currentPatch%total_canopy_area - currentPatch%area + currentPatch%nocomp_pft_label, currentPatch%total_canopy_area - (1._r8-imperfect_fraction)*currentPatch%area call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if !sp mode @@ -1517,7 +988,7 @@ subroutine leaf_area_profile( currentSite ) ! !USES: use EDtypesMod , only : area, heightmax, n_height_bins - use EDParamsMod, only : dinc_vai, dlower_vai + use EDParamsMod, only : dlower_vai,dinc_vai ! ! !ARGUMENTS @@ -1669,15 +1140,7 @@ subroutine leaf_area_profile( currentSite ) endif if(iv==currentCohort%NV) then - remainder = (currentCohort%treelai + currentCohort%treesai) - & - (dlower_vai(iv) - dinc_vai(iv)) - if(remainder > dinc_vai(iv) )then - write(fates_log(), *)'ED: issue with remainder', & - currentCohort%treelai,currentCohort%treesai,dinc_vai(iv), & - currentCohort%NV,remainder - - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif + remainder = (currentCohort%treelai + currentCohort%treesai) - dlower_vai(iv) else remainder = dinc_vai(iv) end if @@ -2082,7 +1545,7 @@ subroutine update_hlm_dynamics(nsites,sites,fcolumn,bc_out) ! call during the fast timestep sequence if (hlm_use_planthydro.eq.itrue) then - call RecruitWaterStorage(nsites,sites,bc_out) + call RecruitWaterStorage(nsites,sites) end if end subroutine update_hlm_dynamics @@ -2240,9 +1703,6 @@ subroutine UpdateCohortLAI(currentCohort, canopy_layer_tlai, total_canopy_area) ! Update LAI and related variables for a given cohort - ! Uses - use EDParamsMod, only : dlower_vai, dinc_vai - ! Arguments type(fates_cohort_type),intent(inout), target :: currentCohort real(r8), intent(in) :: canopy_layer_tlai(nclmax) ! total leaf area index of each canopy layer @@ -2266,34 +1726,21 @@ subroutine UpdateCohortLAI(currentCohort, canopy_layer_tlai, total_canopy_area) end if ! Number of actual vegetation layers in this cohort's crown - currentCohort%nv = count((currentCohort%treelai+currentCohort%treesai) .gt. dlower_vai(:)) + 1 - - if( currentCohort%nv .ne. minloc(dlower_vai, DIM=1, MASK=(dlower_vai>(currentCohort%treelai+currentCohort%treesai))) ) then - write(fates_log(),*) 'We use two methods of finding maximum leaf layers, and they are not equivalent' - write(fates_log(),*) 'count method:',currentCohort%nv - write(fates_log(),*) 'minloc method:',minloc(dlower_vai, DIM=1, MASK=(dlower_vai>(currentCohort%treelai+currentCohort%treesai))) - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if + currentCohort%nv = GetNVegLayers(currentCohort%treelai+currentCohort%treesai) end subroutine UpdateCohortLAI ! =============================================================================================== - function NumPotentialCanopyLayers(currentPatch,site_spread,include_substory) result(z) + function NumCanopyLayers(currentPatch) result(z) ! -------------------------------------------------------------------------------------------- ! Calculate the number of canopy layers in this patch. ! This simple call only determines total layering by querying the cohorts ! which layer they are in, it doesn't do any size evaluation. - ! It may also, optionally, account for the temporary "substory", which is the imaginary - ! layer below the understory which will be needed to temporarily accomodate demotions from - ! the understory in the event the understory has reached maximum allowable area. ! -------------------------------------------------------------------------------------------- - type(fates_patch_type),target :: currentPatch - real(r8),intent(in) :: site_spread - logical :: include_substory - + type(fates_patch_type) :: currentPatch type(fates_cohort_type),pointer :: currentCohort integer :: z @@ -2307,31 +1754,6 @@ function NumPotentialCanopyLayers(currentPatch,site_spread,include_substory) res currentCohort => currentCohort%shorter enddo - if(include_substory)then - arealayer = 0.0 - currentCohort => currentPatch%tallest - do while (associated(currentCohort)) - if(currentCohort%canopy_layer == z) then - call carea_allom(currentCohort%dbh,currentCohort%n,site_spread,currentCohort%pft, & - currentCohort%crowndamage, c_area) - arealayer = arealayer + c_area - end if - currentCohort => currentCohort%shorter - enddo - - ! Does the bottom layer have more than a full canopy? - ! If so we need to make another layer. - if(arealayer > currentPatch%area)then - z = z + 1 - if(hlm_use_sp.eq.itrue)then - if(debug)then - write(fates_log(),*) 'SPmode, canopy_layer full:',arealayer,currentPatch%area - end if - end if - - endif - end if - - end function NumPotentialCanopyLayers + end function NumCanopyLayers end module EDCanopyStructureMod diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index 6d9f8cbcb5..fca123a68d 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -39,6 +39,7 @@ Module EDCohortDynamicsMod use PRTGenericMod , only : num_elements use FatesConstantsMod , only : leaves_off use FatesConstantsMod , only : leaves_shedding + use FatesConstantsMod , only : ihard_season_decid use FatesConstantsMod , only : ihard_stress_decid use FatesConstantsMod , only : isemi_stress_decid use EDParamsMod , only : ED_val_cohort_age_fusion_tol @@ -339,12 +340,13 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ terminate = itrue termination_type = i_term_mort_type_numdens if ( debug ) then - write(fates_log(),*) 'terminating cohorts 0',currentCohort%n/currentPatch%area,currentCohort%dbh,currentCohort%pft,call_index + write(fates_log(),*) 'terminating cohorts 0',currentCohort%n/currentPatch%area, & + currentCohort%dbh,currentCohort%pft,call_index endif endif ! The rest of these are only allowed if we are not dealing with a recruit (level 2) - if (.not.currentCohort%isnew .and. level == 2) then + if_level_2: if (.not.currentCohort%isnew .and. level == 2) then ! Not enough n or dbh if (currentCohort%n/currentPatch%area <= min_npm2 .or. & ! @@ -353,18 +355,13 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ terminate = itrue termination_type = i_term_mort_type_numdens if ( debug ) then - write(fates_log(),*) 'terminating cohorts 1',currentCohort%n/currentPatch%area,currentCohort%dbh,currentCohort%pft,call_index + write(fates_log(),*) 'terminating cohorts 1', & + currentCohort%n/currentPatch%area,currentCohort%dbh, & + currentCohort%pft,call_index endif endif - ! Outside the maximum canopy layer - if (currentCohort%canopy_layer > nclmax ) then - terminate = itrue - termination_type = i_term_mort_type_canlev - if ( debug ) then - write(fates_log(),*) 'terminating cohorts 2', currentCohort%canopy_layer,currentCohort%pft,call_index - endif - endif + ! live biomass pools are terminally depleted if ( ( sapw_c+leaf_c+fnrt_c ) < 1e-10_r8 .or. & @@ -386,8 +383,18 @@ subroutine terminate_cohorts( currentSite, currentPatch, level , call_index, bc_ struct_c,sapw_c,leaf_c,fnrt_c,store_c,currentCohort%pft,call_index endif - endif - endif ! if (.not.currentCohort%isnew .and. level == 2) then + endif + + end if if_level_2 + + ! Outside the maximum canopy layer + if (currentCohort%canopy_layer > nclmax .and. level == 3) then + terminate = itrue + termination_type = i_term_mort_type_canlev + if ( debug ) then + write(fates_log(),*) 'terminating cohorts 2', currentCohort%canopy_layer,currentCohort%pft,call_index + endif + endif if (terminate == itrue) then call terminate_cohort(currentSite, currentPatch, currentCohort, bc_in, termination_type) @@ -940,7 +947,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%size_class,currentCohort%size_by_pft_class) if(hlm_use_planthydro.eq.itrue) then - call FuseCohortHydraulics(currentSite,currentCohort,nextc,bc_in,newn) + call FuseCohortHydraulics(currentSite,currentCohort,nextc,newn) endif ! recent canopy history @@ -1019,6 +1026,12 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) currentCohort%fire_mort = (currentCohort%n*currentCohort%fire_mort + & nextc%n*nextc%fire_mort)/newn + + currentCohort%nonrx_fire_mort = (currentCohort%n*currentCohort%nonrx_fire_mort + & + nextc%n*nextc%nonrx_fire_mort)/newn + + currentCohort%rx_fire_mort = (currentCohort%n*currentCohort%rx_fire_mort + & + nextc%n*nextc%rx_fire_mort)/newn ! mortality diagnostics currentCohort%cmort = (currentCohort%n*currentCohort%cmort + nextc%n*nextc%cmort)/newn @@ -1117,7 +1130,7 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) ! update hydraulics quantities that are functions of height & biomasses ! deallocate the hydro structure of nextc if (hlm_use_planthydro.eq.itrue) then - call UpdateSizeDepPlantHydProps(currentSite,currentCohort, bc_in) + call UpdateSizeDepPlantHydProps(currentSite,currentCohort) endif call nextc%FreeMemory() @@ -1389,10 +1402,10 @@ subroutine DamageRecovery(csite,cpatch,ccohort,newly_recovered) !--- Set some logical flags to simplify "if" blocks is_hydecid_dormant = & - any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid] ) & + any(prt_params%phen_leaf_habit(ipft) == [ihard_stress_decid,isemi_stress_decid] ) & .and. any(ccohort%status_coh == [leaves_off,leaves_shedding] ) is_sedecid_dormant = & - ( prt_params%season_decid(ipft) == itrue ) & + ( prt_params%phen_leaf_habit(ipft) == ihard_season_decid ) & .and. any(ccohort%status_coh == [leaves_off,leaves_shedding] ) ! If plants are drought deciduous and are losing or lost all leaves, they cannot diff --git a/biogeochem/EDLoggingMortalityMod.F90 b/biogeochem/EDLoggingMortalityMod.F90 index 82667f1fea..2d7765c378 100644 --- a/biogeochem/EDLoggingMortalityMod.F90 +++ b/biogeochem/EDLoggingMortalityMod.F90 @@ -1145,9 +1145,6 @@ subroutine logging_litter_fluxes(currentSite, currentPatch, newPatch, patch_site ! This portion is known as "trunk_product_site if(element_id .eq. carbon12_element) then - currentSite%resources_management%trunk_product_site = & - currentSite%resources_management%trunk_product_site + & - trunk_product_site currentSite%resources_management%delta_litter_stock = & currentSite%resources_management%delta_litter_stock + & diff --git a/biogeochem/EDMortalityFunctionsMod.F90 b/biogeochem/EDMortalityFunctionsMod.F90 index 053b1ca3bc..312b01bb83 100644 --- a/biogeochem/EDMortalityFunctionsMod.F90 +++ b/biogeochem/EDMortalityFunctionsMod.F90 @@ -16,6 +16,7 @@ module EDMortalityFunctionsMod use FatesConstantsMod , only : cstarvation_model_lin use FatesConstantsMod , only : cstarvation_model_exp use FatesConstantsMod , only : nearzero + use FatesConstantsMod , only : ihard_season_decid use FatesConstantsMod , only : ihard_stress_decid use FatesConstantsMod , only : isemi_stress_decid use FatesConstantsMod , only : leaves_off @@ -112,11 +113,9 @@ subroutine mortality_rates( cohort_in,bc_in, btran_ft, mean_temp, & ! the future we could accelerate senescence to avoid mortality. Note that both drought ! deciduous and cold deciduous are considered here to be consistent with the idea that ! plants without leaves cannot die of hydraulic failure. - is_decid_dormant = & ! - ( prt_params%stress_decid(cohort_in%pft) == ihard_stress_decid .or. & ! Drought deciduous - prt_params%stress_decid(cohort_in%pft) == isemi_stress_decid .or. & ! Semi-deciduous - prt_params%season_decid(cohort_in%pft) == itrue ) .and. & ! Cold deciduous - ( cohort_in%status_coh == leaves_off ) ! ! Fully abscised + is_decid_dormant = & ! + any ( prt_params%phen_leaf_habit(cohort_in%pft) == [ihard_season_decid,ihard_stress_decid,isemi_stress_decid]) .and. & ! Deciduous + ( cohort_in%status_coh == leaves_off ) ! ! Fully abscised ! Size Dependent Senescence ! rate (r) and inflection point (ip) define the increase in mortality rate with dbh diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index b37b080ea4..6a4fa1034a 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -17,7 +17,6 @@ module EDPatchDynamicsMod use FatesLitterMod , only : litter_type use FatesConstantsMod , only : n_dbh_bins use FatesLitterMod , only : adjust_SF_CWD_frac - use EDTypesMod , only : homogenize_seed_pfts use EDTypesMod , only : area use FatesConstantsMod , only : patchfusion_dbhbin_loweredges use EDtypesMod , only : force_patchfuse_min_biomass @@ -432,7 +431,7 @@ subroutine disturbance_rates( site_in, bc_in) endif ! Fire Disturbance Rate - currentPatch%disturbance_rates(dtype_ifire) = currentPatch%frac_burnt + currentPatch%disturbance_rates(dtype_ifire) = currentPatch%frac_burnt ! Fires can't burn the whole patch, as this causes /0 errors. @@ -482,7 +481,7 @@ end subroutine disturbance_rates ! ============================================================================ - subroutine spawn_patches( currentSite, bc_in) + subroutine spawn_patches( currentSite, bc_in ) ! ! !DESCRIPTION: ! In this subroutine, the following happens, @@ -972,23 +971,61 @@ subroutine spawn_patches( currentSite, bc_in) ! due to fire, as well as from each fire mortality term currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & - nc%n * currentCohort%fire_mort / hlm_freq_day + nc%n * currentCohort%fire_mort / hlm_freq_day ! total + + currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_fire_mort / hlm_freq_day ! for prescribed fire + + currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_canopy(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_fire_mort / hlm_freq_day ! for wildfire fire currentSite%fmort_carbonflux_canopy(currentCohort%pft) = & currentSite%fmort_carbonflux_canopy(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 + currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) = & + currentSite%rx_fmort_carbonflux_canopy(currentCohort%pft) + & + (nc%n * currentCohort%rx_fire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) = & + currentSite%nonrx_fmort_carbonflux_canopy(currentCohort%pft) + & + (nc%n * currentCohort%nonrx_fire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + else ! understory currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & currentSite%fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%fire_mort / hlm_freq_day + + currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_fire_mort / hlm_freq_day + + currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_ustory(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_fire_mort / hlm_freq_day currentSite%fmort_carbonflux_ustory(currentCohort%pft) = & currentSite%fmort_carbonflux_ustory(currentCohort%pft) + & (nc%n * currentCohort%fire_mort) * & total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) = & + currentSite%rx_fmort_carbonflux_ustory(currentCohort%pft) + & + (nc%n * currentCohort%rx_fire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + + currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) = & + currentSite%nonrx_fmort_carbonflux_ustory(currentCohort%pft) + & + (nc%n * currentCohort%nonrx_fire_mort) * & + total_c * g_per_kg * days_per_sec * ha_per_m2 + + end if currentSite%fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & @@ -997,6 +1034,19 @@ subroutine spawn_patches( currentSite, bc_in) ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & leaf_c ) * & g_per_kg * days_per_sec * ha_per_m2 + + currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & + (nc%n * currentCohort%rx_fire_mort) * & + ( (sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & + leaf_c ) * & + g_per_kg * days_per_sec * ha_per_m2 + + currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_abg_flux(currentCohort%size_class, currentCohort%pft) + & + (nc%n * currentCohort%nonrx_fire_mort) * & + ((sapw_c + struct_c + store_c) * prt_params%allom_agb_frac(currentCohort%pft) + & + leaf_c) * g_per_kg * days_per_sec * ha_per_m2 currentSite%fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & @@ -1006,6 +1056,20 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & nc%n * currentCohort%crownfire_mort / hlm_freq_day + currentSite%rx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_cambial_mort / hlm_freq_day + currentSite%rx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & + currentSite%rx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%rx_crown_mort / hlm_freq_day + + currentSite%nonrx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_cambial(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_cambial_mort / hlm_freq_day + currentSite%nonrx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) = & + currentSite%nonrx_fmort_rate_crown(currentCohort%size_class, currentCohort%pft) + & + nc%n * currentCohort%nonrx_crown_mort / hlm_freq_day + ! loss of individual from fire in new patch. nc%n = nc%n * (1.0_r8 - currentCohort%fire_mort) @@ -1039,12 +1103,18 @@ subroutine spawn_patches( currentSite, bc_in) if( (leaf_burn_frac < 0._r8) .or. & (leaf_burn_frac > 1._r8) .or. & - (currentCohort%fire_mort < 0._r8) .or. & - (currentCohort%fire_mort > 1._r8)) then + (currentCohort%fire_mort < 0._r8) .or. & + (currentCohort%fire_mort > 1._r8) .or. & + (currentCohort%rx_fire_mort < 0._r8) .or. & + (currentCohort%rx_fire_mort > 1._r8) .or. & + (currentCohort%nonrx_fire_mort < 0._r8) .or. & + (currentCohort%nonrx_fire_mort > 1._r8) ) then write(fates_log(),*) 'unexpected fire fractions' write(fates_log(),*) prt_params%woody(currentCohort%pft) write(fates_log(),*) leaf_burn_frac write(fates_log(),*) currentCohort%fire_mort + write(fates_log(),*) currentCohort%rx_fire_mort + write(fates_log(),*) currentCohort%nonrx_fire_mort call endrun(msg=errMsg(sourcefile, __LINE__)) end if @@ -1068,14 +1138,16 @@ subroutine spawn_patches( currentSite, bc_in) currentSite%mass_balance(el)%burn_flux_to_atm + & leaf_burn_frac * leaf_m * nc%n - ! This diagnostic only tracks + ! This term increments the loss flux from surviving trees currentSite%flux_diags%elem(el)%burned_liveveg = & currentSite%flux_diags%elem(el)%burned_liveveg + & leaf_burn_frac * leaf_m * nc%n * area_inv + end do - ! Here the mass is removed from the plant + ! Add burned leaf carbon to the atmospheric carbon flux + ! for burning. if(int(prt_params%woody(currentCohort%pft)) == itrue)then call PRTBurnLosses(nc%prt, leaf_organ, leaf_burn_frac) @@ -1866,7 +1938,6 @@ subroutine TransLitterNewPatch(currentSite, & ! by current patch integer, intent(in) :: dist_type ! disturbance type - ! locals type(site_massbal_type), pointer :: site_mass type(litter_type),pointer :: curr_litt ! litter object for current patch @@ -1895,9 +1966,13 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt => currentPatch%litter(el) new_litt => newPatch%litter(el) - ! Distribute the fragmentation litter flux rates. This is only used for diagnostics - ! at this point. Litter fragmentation has already been passed to the output - ! boundary flux arrays. + ! Distribute the fragmentation litter flux rates. The mean site-level + ! flux rate must be preserved, so when we create new patches + ! from disturbance, we must area weight the contributions of the + ! donor patches. This is because the host model will call + ! FatesSoilBGCFluxMod:FluxIntoLitterPools() which uses these + ! litt%<>_frac() arrays to fill site level output fluxes, and + ! this is called over the next day on the model timestep. do c = 1,ncwd new_litt%ag_cwd_frag(c) = new_litt%ag_cwd_frag(c) + & @@ -1989,9 +2064,8 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass*retain_m2 site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + ! Transfer below ground CWD (none burns) - do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%bg_cwd(c,sl) * patch_site_areadis new_litt%bg_cwd(c,sl) = new_litt%bg_cwd(c,sl) + donatable_mass*donate_m2 @@ -2018,7 +2092,7 @@ subroutine TransLitterNewPatch(currentSite, & curr_litt%leaf_fines(dcmpy) = curr_litt%leaf_fines(dcmpy) + donatable_mass*retain_m2 site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%root_fines(dcmpy,sl) * patch_site_areadis @@ -2088,7 +2162,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in - + ! ! !LOCAL VARIABLES: @@ -2209,8 +2283,8 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & ! Absolute number of dead trees being transfered in with the donated area - num_dead_trees = (currentCohort%fire_mort*currentCohort%n * & - patch_site_areadis/currentPatch%area) + num_dead_trees = (currentCohort%fire_mort * & + currentCohort%n * patch_site_areadis/currentPatch%area) ! Contribution of dead trees to leaf litter donatable_mass = num_dead_trees * (leaf_m+repro_m) * & @@ -2229,8 +2303,6 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - - call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2301,7 +2373,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & currentCohort => currentCohort%taller enddo - end do + end do return end subroutine fire_litter_fluxes @@ -2382,7 +2454,7 @@ subroutine mortality_litter_fluxes(currentSite, currentPatch, & do el = 1,num_elements - + element_id = element_list(el) site_mass => currentSite%mass_balance(el) elflux_diags => currentSite%flux_diags%elem(el) @@ -2542,7 +2614,7 @@ end subroutine mortality_litter_fluxes ! ============================================================================ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis, bc_in, & + newPatch, patch_site_areadis, bc_in, & clearing_matrix_element) ! ! !DESCRIPTION: @@ -2596,8 +2668,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & integer :: pft ! loop index for plant functional types integer :: dcmpy ! loop index for decomposability pool integer :: element_id ! parteh compatible global element index - real(r8) :: trunk_product_site ! flux of carbon in trunk products exported off site [ kgC/site ] - ! (note we are accumulating over the patch, but scale is site level) real(r8) :: woodproduct_mass ! mass that ends up in wood products [kg] !--------------------------------------------------------------------- @@ -2640,9 +2710,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & do el = 1,num_elements - ! Zero some site level accumulator diagnsotics - trunk_product_site = 0.0_r8 - element_id = element_list(el) site_mass => currentSite%mass_balance(el) elflux_diags => currentSite%flux_diags%elem(el) @@ -2702,7 +2769,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & end do site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - + call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2762,6 +2829,7 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & EDPftvarcon_inst%landusechange_frac_burned(pft) site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass + else ! all other pools can end up as timber products or burn or go to litter donatable_mass = donatable_mass * (1.0_r8-EDPftvarcon_inst%landusechange_frac_exported(pft)) * & (1.0_r8-EDPftvarcon_inst%landusechange_frac_burned(pft)) @@ -2775,9 +2843,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - trunk_product_site = trunk_product_site + & - woodproduct_mass - ! Amount of trunk mass exported off site [kg/m2] elflux_diags%exported_harvest = elflux_diags%exported_harvest + & woodproduct_mass * area_inv @@ -2794,15 +2859,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & currentCohort => currentCohort%taller enddo - ! Update the amount of carbon exported from the site through logging. - - if(element_id .eq. carbon12_element) then - currentSite%resources_management%trunk_product_site = & - currentSite%resources_management%trunk_product_site + & - trunk_product_site - end if - - end do end if clear_veg_if @@ -3204,10 +3260,14 @@ subroutine fuse_2_patches(csite, dp, rp) rp%tau_l = (dp%tau_l*dp%area + rp%tau_l*rp%area) * inv_sum_area rp%tfc_ros = (dp%tfc_ros*dp%area + rp%tfc_ros*rp%area) * inv_sum_area rp%fi = (dp%fi*dp%area + rp%fi*rp%area) * inv_sum_area + rp%nonrx_fi = (dp%nonrx_fi*dp%area + rp%nonrx_fi*rp%area) * inv_sum_area + rp%rx_fi = (dp%rx_fi*dp%area + rp%rx_fi*rp%area) * inv_sum_area rp%fd = (dp%fd*dp%area + rp%fd*rp%area) * inv_sum_area rp%ros_back = (dp%ros_back*dp%area + rp%ros_back*rp%area) * inv_sum_area rp%scorch_ht(:) = (dp%scorch_ht(:)*dp%area + rp%scorch_ht(:)*rp%area) * inv_sum_area rp%frac_burnt = (dp%frac_burnt*dp%area + rp%frac_burnt*rp%area) * inv_sum_area + rp%rx_frac_burnt = (dp%rx_frac_burnt*dp%area + rp%rx_frac_burnt*rp%area) * inv_sum_area + rp%nonrx_frac_burnt = (dp%nonrx_frac_burnt*dp%area + rp%nonrx_frac_burnt*rp%area) * inv_sum_area rp%btran_ft(:) = (dp%btran_ft(:)*dp%area + rp%btran_ft(:)*rp%area) * inv_sum_area rp%zstar = (dp%zstar*dp%area + rp%zstar*rp%area) * inv_sum_area rp%c_stomata = (dp%c_stomata*dp%area + rp%c_stomata*rp%area) * inv_sum_area @@ -3574,12 +3634,8 @@ subroutine DistributeSeeds(currentSite,seed_mass,el,pft) do while(associated(currentPatch)) litt => currentPatch%litter(el) - if(homogenize_seed_pfts) then - litt%seed(:) = litt%seed(:) + seed_mass/(area_site*real(numpft,r8)) - else - litt%seed(pft) = litt%seed(pft) + seed_mass/area_site - end if - + litt%seed(pft) = litt%seed(pft) + seed_mass/area_site + currentPatch => currentPatch%younger end do diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 3b1d61e914..f509aec000 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -18,7 +18,8 @@ module EDPhysiologyMod use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : hlm_use_fixed_biogeog use FatesInterfaceTypesMod, only : hlm_use_nocomp - use EDParamsMod , only : crop_lu_pft_vector + use EDParamsMod , only : crop_lu_pft_vector + use EDParamsMod , only : GetNVegLayers use FatesInterfaceTypesMod, only : hlm_nitrogen_spec use FatesInterfaceTypesMod, only : hlm_phosphorus_spec use FatesInterfaceTypesMod, only : hlm_use_tree_damage @@ -66,6 +67,8 @@ module EDPhysiologyMod use EDParamsMod , only : nclmax use EDTypesMod , only : AREA,AREA_INV use FatesConstantsMod , only : leaves_shedding + use FatesConstantsMod , only : ievergreen + use FatesConstantsMod , only : ihard_season_decid use FatesConstantsMod , only : ihard_stress_decid use FatesConstantsMod , only : isemi_stress_decid use EDParamsMod , only : nlevleaf @@ -144,7 +147,8 @@ module EDPhysiologyMod use PRTInitParamsFatesMod, only : NewRecruitTotalStoichiometry use FatesInterfaceTypesMod, only : hlm_use_luh use FatesInterfaceTypesMod, only : hlm_regeneration_model - + + implicit none private @@ -155,7 +159,6 @@ module EDPhysiologyMod public :: calculate_SP_properties public :: recruitment public :: ZeroLitterFluxes - public :: ZeroAllocationRates public :: PreDisturbanceLitterFluxes public :: PreDisturbanceIntegrateLitter @@ -257,13 +260,12 @@ end subroutine ZeroAllocationRates ! ============================================================================ - subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) + subroutine GenerateDamageAndLitterFluxes( csite, cpatch ) ! Arguments type(ed_site_type) :: csite type(fates_patch_type) :: cpatch - type(bc_in_type), intent(in) :: bc_in - + ! Locals type(fates_cohort_type), pointer :: ccohort ! Current cohort @@ -437,8 +439,7 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) ! associated with seed turnover, seed influx, litterfall from live and ! dead plants, germination, and fragmentation. ! - ! At this time we do not have explicit herbivory, and burning losses to litter - ! are handled elsewhere. + ! Herbivory is handled here. burning losses to litter are handled elsewhere. ! ! Note: The processes conducted here DO NOT handle litter fluxes associated ! with disturbance. Those fluxes are handled elsewhere (EDPatchDynamcisMod) @@ -471,12 +472,12 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in ) diag => currentSite%flux_diags%elem(el)) ! Calculate loss rate of viable seeds to litter - call SeedDecay(litt, currentPatch, bc_in) + call SeedDecay(litt, currentPatch) ! Calculate seed germination rate, the status flags prevent ! germination from occuring when the site is in a drought ! (for drought deciduous) or too cold (for cold deciduous) - call SeedGermination(litt, currentSite%cstatus, currentSite%dstatus(1:numpft), bc_in, currentPatch) + call SeedGermination(litt, currentSite%cstatus, currentSite%dstatus(1:numpft), currentPatch) ! Send fluxes from newly created litter into the litter pools ! This litter flux is from non-disturbance inducing mortality, as well @@ -629,14 +630,11 @@ subroutine trim_canopy( currentSite ) real(r8) :: sapw_c ! sapwood carbon [kg] real(r8) :: store_c ! storage carbon [kg] real(r8) :: struct_c ! structure carbon [kg] - real(r8) :: leaf_inc ! LAI-only portion of the vegetation increment of dinc_vai real(r8) :: lai_canopy_above ! the LAI in the canopy layers above the layer of interest - real(r8) :: lai_layers_above ! the LAI in the leaf layers, within the current canopy, - ! above the leaf layer of interest - real(r8) :: lai_current ! the LAI in the current leaf layer real(r8) :: cumulative_lai ! whole canopy cumulative LAI, top down, to the leaf layer of interest real(r8) :: cumulative_lai_cohort ! cumulative LAI within the current cohort only - + real(r8) :: leaf_veg_frac ! fraction of vegetation area (leaf+stem) that is just leaf + ! Temporary diagnostic ouptut ! LAPACK linear least squares fit variables @@ -704,15 +702,10 @@ subroutine trim_canopy( currentSite ) currentCohort%dbh, currentCohort%crowndamage, currentCohort%canopy_trim, & currentCohort%efstem_coh, 0, currentCohort%treelai, currentCohort%treesai ) - currentCohort%nv = count((currentCohort%treelai+currentCohort%treesai) .gt. dlower_vai(:)) + 1 - - if (currentCohort%nv > nlevleaf)then - write(fates_log(),*) 'nv > nlevleaf',currentCohort%nv, & - currentCohort%treelai,currentCohort%treesai, & - currentCohort%c_area,currentCohort%n,leaf_c - call endrun(msg=errMsg(sourcefile, __LINE__)) - endif + currentCohort%nv = GetNVegLayers(currentCohort%treelai+currentCohort%treesai) + leaf_veg_frac = currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) + ! Find target leaf biomass. Here we assume that leaves would be fully flushed ! (elongation factor = 1) call bleaf(currentcohort%dbh,ipft,& @@ -746,20 +739,18 @@ subroutine trim_canopy( currentSite ) !Leaf cost vs net uptake for each leaf layer. do z = 1, currentCohort%nv - ! Calculate the cumulative total vegetation area index (no snow occlusion, stems and leaves) - leaf_inc = dinc_vai(z) * & - currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) - - ! Now calculate the cumulative top-down lai of the current layer's midpoint within the current cohort - lai_layers_above = (dlower_vai(z) - dinc_vai(z)) * & - currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) - lai_current = min(leaf_inc, currentCohort%treelai - lai_layers_above) - cumulative_lai_cohort = lai_layers_above + 0.5*lai_current - - ! Now add in the lai above the current cohort for calculating the sla leaf level lai_canopy_above = sum(currentPatch%canopy_layer_tlai(1:cl-1)) - cumulative_lai = lai_canopy_above + cumulative_lai_cohort + + if(z == currentCohort%nv) then + cumulative_lai_cohort = leaf_veg_frac * & + (dlower_vai(z)+0.5_r8*(currentCohort%treelai+currentCohort%treesai-dlower_vai(z))) + else + cumulative_lai_cohort = leaf_veg_frac * & + (dlower_vai(z)+0.5_r8*dinc_vai(z)) + end if + cumulative_lai = cumulative_lai_cohort + lai_canopy_above + ! There was activity this year in this leaf layer. This should only occur for bottom most leaf layer if (currentCohort%year_net_uptake(z) /= 999._r8)then @@ -775,20 +766,21 @@ subroutine trim_canopy( currentSite ) sla_levleaf = min(sla_max,prt_params%slatop(ipft)/nscaler_levleaf) ! Find the realised leaf lifespan, depending on the leaf phenology. - if (prt_params%season_decid(ipft) == itrue) then + select case (prt_params%phen_leaf_habit(ipft)) + case (ihard_season_decid) ! Cold-deciduous costs. Assume time-span to be 1 year to be consistent ! with FATES default pft_leaf_lifespan = decid_leaf_long_max - elseif (any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid]) )then + case (ihard_stress_decid,isemi_stress_decid) ! Drought-decidous costs. Assume time-span to be the least between ! 1 year and the life span provided by the parameter file. pft_leaf_lifespan = & min(decid_leaf_long_max,leaf_long) - else !evergreen costs + case (ievergreen) !evergreen costs pft_leaf_lifespan = leaf_long - end if + end select ! Leaf cost at leaf level z (kgC m-2 year-1) accounting for sla profile ! (Convert from SLA in m2g-1 to m2kg-1) @@ -828,7 +820,7 @@ subroutine trim_canopy( currentSite ) ! Check leaf cost against the yearly net uptake for that cohort leaf layer if (currentCohort%year_net_uptake(z) < currentCohort%leaf_cost) then ! Make sure the cohort trim fraction is great than the pft trim limit - if (currentCohort%canopy_trim > EDPftvarcon_inst%trim_limit(ipft)) then + if (currentCohort%canopy_trim > (EDPftvarcon_inst%trim_limit(ipft) + EDPftvarcon_inst%trim_inc(ipft))) then ! keep trimming until none of the canopy is in negative carbon balance. if (currentCohort%height > EDPftvarcon_inst%hgt_min(ipft)) then @@ -877,11 +869,14 @@ subroutine trim_canopy( currentSite ) optimum_trim = (nnu_clai_b(1,1) / cumulative_lai_cohort) * initial_trim ! Determine if the optimum trim value makes sense. The smallest cohorts tend to have unrealistic fits. - if (optimum_trim > 0. .and. optimum_trim < 1.) then + if (optimum_trim > EDPftvarcon_inst%trim_limit(ipft) .and. optimum_trim < 1.) then currentCohort%canopy_trim = optimum_trim trimmed = .true. + else if (optimum_trim <= EDPftvarcon_inst%trim_limit(ipft)) then + currentCohort%canopy_trim = EDPftvarcon_inst%trim_limit(ipft) + trimmed = .true. endif endif endif @@ -1298,7 +1293,7 @@ subroutine phenology( currentSite, bc_in ) ! the leaf biomass will be capped at 40% of the biomass the cohort would have if ! it were in well-watered conditions. !---~--- - case_drought_phen: select case (prt_params%stress_decid(ipft)) + case_drought_phen: select case (prt_params%phen_leaf_habit(ipft)) case (ihard_stress_decid) !---~--- ! Default ("hard") drought deciduous phenology. The decision on whether to @@ -1517,11 +1512,11 @@ subroutine phenology( currentSite, bc_in ) ! Assign elongation factors for non-drought deciduous PFTs, which will be used ! to define the cohort status. - case_cold_phen: select case(prt_params%season_decid(ipft)) - case (ifalse) + case_cold_phen: select case(prt_params%phen_leaf_habit(ipft)) + case (ievergreen) ! Evergreen, ensure that elongation factor is always one. currentSite%elong_factor(ipft) = 1.0_r8 - case (itrue) + case (ihard_season_decid) ! Cold-deciduous. Define elongation factor based on cold status select case (currentSite%cstatus) case (phen_cstat_nevercold,phen_cstat_iscold) @@ -1620,7 +1615,8 @@ subroutine phenology_leafonoff(currentSite) ! MLO. To avoid duplicating code for drought and cold deciduous PFTs, we first ! check whether or not it's time to flush or time to shed leaves, then ! use a common code for flushing or shedding leaves. - is_time_block: if (prt_params%season_decid(ipft) == itrue) then ! Cold deciduous + is_time_block: select case (prt_params%phen_leaf_habit(ipft)) + case (ihard_season_decid) ! Cold deciduous ! A. Is this the time for COLD LEAVES to switch to ON? is_flushing_time = ( currentSite%cstatus == phen_cstat_notcold .and. & ! We just moved to leaves being on @@ -1631,7 +1627,7 @@ subroutine phenology_leafonoff(currentSite) ( currentCohort%dbh > EDPftvarcon_inst%phen_cold_size_threshold(ipft) .or. & ! Grasses are big enough or... prt_params%woody(ipft) == itrue ) ! this is a woody PFT. - elseif (any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid]) ) then ! Drought deciduous + case (ihard_stress_decid,isemi_stress_decid) ! Drought deciduous ! A. Is this the time for DROUGHT LEAVES to switch to ON? is_flushing_time = any( currentSite%dstatus(ipft) == [phen_dstat_moiston,phen_dstat_timeon] ) .and. & ! Leaf flushing time (moisture or time) @@ -1640,11 +1636,11 @@ subroutine phenology_leafonoff(currentSite) ! This will be true when leaves are abscissing (partially or fully) due to moisture or time is_shedding_time = any( currentSite%dstatus(ipft) == [phen_dstat_moistoff,phen_dstat_timeoff,phen_dstat_pshed] ) .and. & any( currentCohort%status_coh == [leaves_on,leaves_shedding] ) - else + case (ievergreen) ! This PFT is not deciduous. is_flushing_time = .false. is_shedding_time = .false. - end if is_time_block + end select is_time_block @@ -2056,7 +2052,6 @@ subroutine SeedUpdate( currentSite ) ! !USES: use EDTypesMod, only : area - use EDTypesMod, only : homogenize_seed_pfts use FatesInterfaceTypesMod, only : hlm_seeddisp_cadence use FatesInterfaceTypesMod, only : fates_dispersal_cadence_none ! @@ -2080,6 +2075,11 @@ subroutine SeedUpdate( currentSite ) integer :: el ! loop counter for litter element types integer :: element_id ! element id consistent with parteh/PRTGenericMod.F90 + logical, parameter :: nocomp_seed_localization = .true. ! if nocomp is on, only send a given PFT's seeds to patches of that nocomp PFT + real(r8) :: nocomp_seed_scaling ! scalar to handle case for nocomp_seed_localization + real(r8) :: seed_supply ! external seed rain scalar to handle case for nocomp_seed_localization + real(r8) :: nocomp_patch_areas(0:numpft) ! vector of the total patch areas for each nocomp PFT + ! If the dispersal kernel is not turned on, keep the dispersal fraction at zero site_disp_frac(:) = 0._r8 if (hlm_seeddisp_cadence .ne. fates_dispersal_cadence_none) then @@ -2093,6 +2093,19 @@ subroutine SeedUpdate( currentSite ) site_mass => currentSite%mass_balance(el) + ! If we are in nocomp configuration and we are restricting each PFT's seeds to all fall + ! only on patches that allow that PFT to grow, then we need to add up all the patch areas + ! for each nocomp PFT to normalize the seed fluxes with later. + if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then + nocomp_patch_areas(0:numpft) = 0._r8 + currentPatch => currentSite%oldest_patch + nocomp_patch_loop: do while (associated(currentPatch)) + nocomp_patch_areas(currentPatch%nocomp_pft_label) = nocomp_patch_areas(currentPatch%nocomp_pft_label) & + + currentPatch%area + currentPatch => currentPatch%younger + end do nocomp_patch_loop + endif + ! Loop over all patches and sum up the seed input for each PFT currentPatch => currentSite%oldest_patch seed_rain_loop: do while (associated(currentPatch)) @@ -2134,13 +2147,6 @@ subroutine SeedUpdate( currentSite ) currentPatch => currentPatch%younger enddo seed_rain_loop - ! We can choose to homogenize seeds. This is simple, we just - ! add up all the seed from each pft at the site level, and then - ! equally distribute to the PFT pools - if ( homogenize_seed_pfts ) then - site_seed_rain(1:numpft) = sum(site_seed_rain(:))/real(numpft,r8) - end if - ! Loop over all patches again and disperse the mixed seeds into the input flux ! arrays ! Loop over all patches and sum up the seed input for each PFT @@ -2152,9 +2158,25 @@ subroutine SeedUpdate( currentSite ) if(currentSite%use_this_pft(pft).eq.itrue)then + ! special case: do we want to restrict each PFT's seeds to only go to patches with that nocomp PFT label? + ! If so, then use a normalization factor that is one over the nocomp patch fraction for all patches of + ! that PFT's nocomp label, and zero for all other patches. If we don't do this, then just set scalar to one. + ! Similarly, only add external seed rain to a given PFT's nocomp patches + nocomp_seed_scaling = 1._r8 + seed_supply = EDPftvarcon_inst%seed_suppl(pft) + if (nocomp_seed_localization .and. hlm_use_nocomp .eq. itrue ) then + if (currentPatch%nocomp_pft_label .eq. pft) then + nocomp_seed_scaling = AREA/nocomp_patch_areas(pft) + else + nocomp_seed_scaling = 0._r8 + seed_supply = 0._r8 + endif + endif + ! Seed input from local sources (within site). Note that a fraction of the ! internal seed rain is sent out to neighboring gridcells. - litt%seed_in_local(pft) = litt%seed_in_local(pft) + site_seed_rain(pft)*(1.0_r8-site_disp_frac(pft))/area ![kg/m2/day] + litt%seed_in_local(pft) = litt%seed_in_local(pft) + nocomp_seed_scaling * & + (1.0_r8-site_disp_frac(pft)) * (site_seed_rain(pft)/area) ! site_seed_rain conversion from [kg/site/day -> kg/m2/day] ! If we are using the Tree Recruitment Scheme (TRS) with or w/o seedling dynamics if ( any(hlm_regeneration_model == [TRS_regeneration, TRS_no_seedling_dyn]) .and. & @@ -2186,7 +2208,7 @@ subroutine SeedUpdate( currentSite ) ! Seed input from external sources (user param seed rain, or dispersal model) ! Include both prescribed seed_suppl and seed_in dispersed from neighbouring gridcells - seed_in_external = seed_stoich*(currentSite%seed_in(pft)/area + EDPftvarcon_inst%seed_suppl(pft)*years_per_day) ![kg/m2/day] + seed_in_external = seed_stoich * (seed_supply*years_per_day + currentSite%seed_in(pft)/area) ![kg/m2/day] litt%seed_in_extern(pft) = litt%seed_in_extern(pft) + seed_in_external ! Seeds entering externally [kg/site/day] @@ -2211,7 +2233,7 @@ end subroutine SeedUpdate ! ============================================================================ - subroutine SeedDecay( litt , currentPatch, bc_in ) + subroutine SeedDecay( litt , currentPatch ) ! ! !DESCRIPTION: ! 1. Flux from seed pool into leaf litter pool @@ -2222,7 +2244,6 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) ! !ARGUMENTS type(litter_type) :: litt type(fates_patch_type), intent(in) :: currentPatch ! ahb added this - type(bc_in_type), intent(in) :: bc_in ! ahb added this ! ! !LOCAL VARIABLES: integer :: pft @@ -2328,7 +2349,7 @@ subroutine SeedDecay( litt , currentPatch, bc_in ) end subroutine SeedDecay ! ============================================================================ - subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) + subroutine SeedGermination( litt, cold_stat, drought_stat, currentPatch ) ! ! !DESCRIPTION: ! Flux from seed bank into the seedling pool @@ -2340,7 +2361,6 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) type(litter_type) :: litt integer , intent(in) :: cold_stat ! Is the site in cold leaf-off status? integer, dimension(numpft), intent(in) :: drought_stat ! Is the site in drought leaf-off status? - type(bc_in_type), intent(in) :: bc_in type(fates_patch_type), intent(in) :: currentPatch ! ! !LOCAL VARIABLES: @@ -2430,19 +2450,17 @@ subroutine SeedGermination( litt, cold_stat, drought_stat, bc_in, currentPatch ) litt%seed_germ_in(pft) = litt%seed(pft) * seedling_emerg_rate end if if_tfs_or_def - - !set the germination only under the growing season...c.xu - - if ((prt_params%season_decid(pft) == itrue ) .and. & - (any(cold_stat == [phen_cstat_nevercold,phen_cstat_iscold]))) then - ! no germination for all PFTs when cold - litt%seed_germ_in(pft) = 0.0_r8 - endif - ! Drought deciduous, halt germination when status is shedding, even leaves are not - ! completely abscissed. MLO - select case (prt_params%stress_decid(pft)) + select case (prt_params%phen_leaf_habit(pft)) + case (ihard_season_decid) + !set the germination only under the growing season...c.xu + if (any(cold_stat == [phen_cstat_nevercold,phen_cstat_iscold])) then + ! no germination for all PFTs when cold + litt%seed_germ_in(pft) = 0.0_r8 + end if case (ihard_stress_decid,isemi_stress_decid) + ! Drought deciduous, halt germination when status is shedding, even leaves are not + ! completely abscissed. MLO if (any(drought_stat(pft) == [phen_dstat_timeoff,phen_dstat_moistoff,phen_dstat_pshed])) then litt%seed_germ_in(pft) = 0.0_r8 end if @@ -2551,23 +2569,24 @@ subroutine recruitment(currentSite, currentPatch, bc_in) efstem_coh = 1.0_r8 leaf_status = leaves_on - ! but if the plant is seasonally (cold) deciduous, and the site status is flagged - ! as "cold", then set the cohort's status to leaves_off, and remember the leaf biomass - if ((prt_params%season_decid(ft) == itrue) .and. & - (any(currentSite%cstatus == [phen_cstat_nevercold, phen_cstat_iscold]))) then - efleaf_coh = 0.0_r8 - effnrt_coh = 1.0_r8 - fnrt_drop_fraction - efstem_coh = 1.0_r8 - stem_drop_fraction - leaf_status = leaves_off - end if - - ! Or.. if the plant is drought deciduous, make sure leaf status is consistent with the - ! leaf elongation factor. - ! For tissues other than leaves, the actual drop fraction is a combination of the - ! elongation factor (e) and the drop fraction (x), which will ensure that the remaining - ! tissue biomass will be exactly e when x=1, and exactly the original biomass when x = 0. - select case (prt_params%stress_decid(ft)) + ! look for cases in which leaves should be off + select case (prt_params%phen_leaf_habit(ft)) + case (ihard_season_decid) + select case(currentSite%cstatus) + case (phen_cstat_nevercold, phen_cstat_iscold) + ! If the plant is seasonally (cold) deciduous, and the site status is flagged + ! as "cold", then set the cohort's status to leaves_off. + efleaf_coh = 0.0_r8 + effnrt_coh = 1.0_r8 - fnrt_drop_fraction + efstem_coh = 1.0_r8 - stem_drop_fraction + leaf_status = leaves_off + end select case (ihard_stress_decid, isemi_stress_decid) + ! If the plant is drought deciduous, make sure leaf status is consistent with the + ! leaf elongation factor. + ! For tissues other than leaves, the actual drop fraction is a combination of the + ! elongation factor (e) and the drop fraction (x), which will ensure that the remaining + ! tissue biomass will be exactly e when x=1, and exactly the original biomass when x = 0. efleaf_coh = currentSite%elong_factor(ft) effnrt_coh = 1.0_r8 - (1.0_r8 - efleaf_coh)*fnrt_drop_fraction efstem_coh = 1.0_r8 - (1.0_r8 - efleaf_coh)*stem_drop_fraction @@ -2955,7 +2974,7 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) elflux_diags%root_litter_input(pft) + & (fnrt_m_turnover + store_m_turnover ) * currentCohort%n - ! send the part of the herbivory flux that doesn't go to litter to the atmosphere + ! send the part of the herbivory flux that doesn't go to litter to the atmosphere (and also for tracking) site_mass%herbivory_flux_out = & site_mass%herbivory_flux_out + & @@ -3130,10 +3149,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in) (leaf_m + fnrt_m + store_m ) * & (dead_n_ilogging+dead_n_dlogging) *currentPatch%area - currentSite%resources_management%trunk_product_site = & - currentSite%resources_management%trunk_product_site + & - trunk_wood * logging_export_frac * currentPatch%area - do c = 1,ncwd currentSite%resources_management%delta_litter_stock = & currentSite%resources_management%delta_litter_stock + & diff --git a/biogeochem/FatesAllometryMod.F90 b/biogeochem/FatesAllometryMod.F90 index 28cd59dde3..d7e09393d4 100644 --- a/biogeochem/FatesAllometryMod.F90 +++ b/biogeochem/FatesAllometryMod.F90 @@ -860,7 +860,11 @@ subroutine tree_lai_sai(leaf_c, pft, c_area, nplant, cl, canopy_lai, vcmax25top, real(r8), intent(out) :: treelai ! plant LAI [m2 leaf area/m2 crown area] real(r8), intent(out) :: treesai ! plant SAI [m2 stem area/m2 crown area] - + + ! If this is true, prevent plants from exceeding the maximum VAI + ! that is specified in the dinc_vai array. + logical, parameter :: do_vai_capping = .true. + treelai = tree_lai( leaf_c, pft, c_area, nplant, cl, canopy_lai, vcmax25top) @@ -868,12 +872,13 @@ subroutine tree_lai_sai(leaf_c, pft, c_area, nplant, cl, canopy_lai, vcmax25top, cl, canopy_lai, treelai, vcmax25top, call_id ) ! Don't allow lai+sai to exceed the vertical discretization bounds - if( (treelai + treesai) > (sum(dinc_vai)) )then - treelai = sum(dinc_vai) * (1._r8 - prt_params%allom_sai_scaler(pft)) - nearzero - treesai = sum(dinc_vai) * prt_params%allom_sai_scaler(pft) - nearzero + if( do_vai_capping ) then + if( (treelai + treesai) > (sum(dinc_vai)) )then + treelai = sum(dinc_vai) * (1._r8 - prt_params%allom_sai_scaler(pft)) - nearzero + treesai = sum(dinc_vai) * prt_params%allom_sai_scaler(pft) - nearzero + end if end if - return end subroutine tree_lai_sai @@ -3177,22 +3182,21 @@ subroutine VegAreaLayer(tree_lai,tree_sai,tree_height,iv,nv,pft,snow_depth, & if_any_vai: if(tree_vai>0._r8)then + ! This function will return the total VAI of the cohort + ! if an index of 0 is passed in... if(iv==0)then vai_top = 0.0 vai_bot = tree_vai else - if(iv>1)then - vai_top = dlower_vai(iv) - dinc_vai(iv) - else - vai_top = 0._r8 - end if - - if(iv fates_endrun use FatesGlobals, only : fates_log @@ -142,7 +144,7 @@ module FatesCohortMod real(r8) :: gpp_tstep ! Gross Primary Production (see above *) real(r8) :: gpp_acc real(r8) :: gpp_acc_hold - + real(r8) :: npp_acc real(r8) :: npp_acc_hold @@ -154,7 +156,7 @@ module FatesCohortMod real(r8) :: c13disc_clm ! carbon 13 discrimination in new synthesized carbon at each indiv/timestep [ppm] real(r8) :: c13disc_acc ! carbon 13 discrimination in new synthesized carbon at each indiv/day ! at the end of a day [ppm] - + ! The following four biophysical rates are assumed to be at the canopy top, at reference temp 25degC, ! and based on the leaf age weighted average of the PFT parameterized values. ! The last condition is why it is dynamic and tied to the cohort @@ -271,6 +273,12 @@ module FatesCohortMod real(r8) :: crownfire_mort ! probability of tree post-fire mortality from crown scorch [0-1] ! (conditional on the tree being subjected to the fire) real(r8) :: fire_mort ! post-fire mortality from cambial and crown damage assuming two are independent [0-1] + real(r8) :: nonrx_cambial_mort ! cambial kill mortality due to wildfire + real(r8) :: nonrx_crown_mort ! crown fire mortality due to wildfire + real(r8) :: nonrx_fire_mort ! post-fire mortality due to wildfire + real(r8) :: rx_cambial_mort ! cambial kill mortality due to prescribed fire + real(r8) :: rx_crown_mort ! crown fire mortality due to prescribed fire + real(r8) :: rx_fire_mort ! post-fire mortality due to prescribed fire !--------------------------------------------------------------------------- @@ -286,6 +294,7 @@ module FatesCohortMod procedure :: Copy procedure :: FreeMemory procedure :: CanUpperUnder + procedure, public :: SumMortForHistory procedure :: InitPRTBoundaryConditions procedure :: UpdateCohortBioPhysRates procedure :: Dump @@ -449,7 +458,13 @@ subroutine NanValues(this) this%cambial_mort = nan this%crownfire_mort = nan this%fire_mort = nan - + this%nonrx_cambial_mort = nan + this%nonrx_crown_mort = nan + this%nonrx_fire_mort = nan + this%rx_cambial_mort = nan + this%rx_crown_mort = nan + this%rx_fire_mort = nan + end subroutine NanValues !=========================================================================== @@ -496,7 +511,7 @@ subroutine ZeroValues(this) this%c13disc_clm = 0._r8 this%c13disc_acc = 0._r8 - + this%ts_net_uptake(:) = 0._r8 this%year_net_uptake(:) = 999._r8 ! this needs to be 999, or trimming of new cohorts will break. @@ -535,6 +550,12 @@ subroutine ZeroValues(this) this%cambial_mort = 0._r8 this%crownfire_mort = 0._r8 this%fire_mort = 0._r8 + this%nonrx_cambial_mort = 0._r8 + this%nonrx_crown_mort = 0._r8 + this%nonrx_fire_mort = 0._r8 + this%rx_cambial_mort = 0._r8 + this%rx_crown_mort = 0._r8 + this%rx_fire_mort = 0._r8 end subroutine ZeroValues @@ -780,6 +801,12 @@ subroutine Copy(this, copyCohort) copyCohort%cambial_mort = this%cambial_mort copyCohort%crownfire_mort = this%crownfire_mort copyCohort%fire_mort = this%fire_mort + copyCohort%nonrx_cambial_mort = this%nonrx_cambial_mort + copyCohort%nonrx_crown_mort = this%nonrx_crown_mort + copyCohort%nonrx_fire_mort = this%nonrx_fire_mort + copyCohort%rx_cambial_mort = this%rx_cambial_mort + copyCohort%rx_crown_mort = this%rx_crown_mort + copyCohort%rx_fire_mort = this%rx_fire_mort ! HYDRAULICS if (hlm_use_planthydro .eq. itrue) then @@ -1001,6 +1028,44 @@ end function CanUpperUnder !=========================================================================== + function SumMortForHistory(this, per_year) result(mort_sum) + ! + ! DESCRIPTION: + ! Sum the various cohort-level mortality variables for saving to history. + ! Units depend on per_year: + ! per_year true: kg m-2 yr-1 + ! per_year false: kg m-2 s-1 + + ! ARGUMENTS: + class(fates_cohort_type) :: this ! current cohort of interest + logical :: per_year + ! + ! VARIABLES + ! Units depend on per_year; see description above. + real(r8) :: mort_natural + real(r8) :: mort_logging + real(r8) :: mort_sum + + ! "Natural" mortality + mort_natural = this%bmort + this%hmort + this%cmort + this%frmort + this%smort + this%asmort + this%dgmort + if (.not. per_year) then + ! Convert kg m-2 yr-1 to kg m-2 s-1 + mort_natural = mort_natural * days_per_sec * years_per_day + end if + + ! Logging mortality + mort_logging = this%lmort_direct + this%lmort_collateral + this%lmort_infra + if (per_year) then + ! Convert kg m-2 s-1 to kg m-2 yr-1 + mort_logging = mort_logging * sec_per_day * days_per_year + end if + + mort_sum = mort_natural + mort_logging + + end function SumMortForHistory + + !=========================================================================== + subroutine Dump(this) ! ! DESCRIPTION: @@ -1080,6 +1145,12 @@ subroutine Dump(this) write(fates_log(),*) 'cohort%fire_mort = ', this%fire_mort write(fates_log(),*) 'cohort%crownfire_mort = ', this%crownfire_mort write(fates_log(),*) 'cohort%cambial_mort = ', this%cambial_mort + write(fates_log(),*) 'cohort%nonrx_cambial_mort = ', this%nonrx_cambial_mort + write(fates_log(),*) 'cohort%nonrx_crown_mort = ', this%nonrx_crown_mort + write(fates_log(),*) 'cohort%nonrx_fire_mort = ', this%nonrx_fire_mort + write(fates_log(),*) 'cohort%rx_crown_mort = ', this%rx_crown_mort + write(fates_log(),*) 'cohort%rx_cambial_mort = ', this%rx_cambial_mort + write(fates_log(),*) 'cohort%rx_fire_mort = ', this%rx_fire_mort write(fates_log(),*) 'cohort%size_class = ', this%size_class write(fates_log(),*) 'cohort%size_by_pft_class = ', this%size_by_pft_class diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 0334dfc989..5c904d0a96 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -21,7 +21,7 @@ module FatesPatchMod use PRTGenericMod, only : struct_organ, leaf_organ, sapw_organ use PRTParametersMod, only : prt_params use FatesConstantsMod, only : nocomp_bareground - use EDParamsMod, only : nlevleaf, nclmax, maxpft + use EDParamsMod, only : nlevleaf, nclmax, maxpft,max_cohort_per_patch use FatesConstantsMod, only : n_dbh_bins, n_dist_types use FatesConstantsMod, only : t_water_freeze_k_1atm use FatesRunningMeanMod, only : ema_24hr, fixed_24hr, ema_lpa, ema_longterm @@ -41,6 +41,26 @@ module FatesPatchMod ! for error message writing character(len=*), parameter :: sourcefile = __FILE__ + type :: fates_cohort_vec_type + + ! This is a scratch array for cohort pointers + ! this is useful if you want to loop over a sparse subset + ! of fates cohorts over and over again, allowing + ! you to iterate them in a do loop + + type(fates_cohort_type), pointer :: p => null() + + ! This is the area of the cohort (less than or equal to cohort%carea) + ! that will be promoted or demoted, ie promoted/demoted crown area + ! units [m2/site] or [m2/ha] (same as the patch area and crown area) + ! We track it here because we construct the cohort list for specific + ! canopy layers + + real(r8) :: pd_area + + end type fates_cohort_vec_type + + type, public :: fates_patch_type ! POINTERS @@ -48,7 +68,8 @@ module FatesPatchMod type (fates_cohort_type), pointer :: shortest => null() ! pointer to patch's shortest cohort type (fates_patch_type), pointer :: older => null() ! pointer to next older patch type (fates_patch_type), pointer :: younger => null() ! pointer to next younger patch - + type (fates_cohort_vec_type), pointer :: co_scr(:) ! Scratch vector of cohort properties + !--------------------------------------------------------------------------- ! INDICES @@ -209,9 +230,19 @@ module FatesPatchMod real(r8) :: ros_back ! rate of backward spread of fire [m/min] real(r8) :: tau_l ! duration of lethal heating [min] real(r8) :: fi ! average fire intensity of flaming front [kJ/m/s] or [kW/m] - integer :: fire ! is there a fire? [1=yes; 0=no] + integer :: fire ! is there a fire (rx + nonrx)? [1=yes; 0=no] real(r8) :: fd ! fire duration [min] - real(r8) :: frac_burnt ! fraction of patch burnt by fire + real(r8) :: frac_burnt ! total fraction of patch burnt by fire (rx + nonrx) + + ! wildfire + real(r8) :: nonrx_fire ! is there a wildfire [1=yes; 0=no] + real(r8) :: nonrx_fi ! average fire intensity of wildfire flaming front + real(r8) :: nonrx_frac_burnt ! fraction burnt by wildfire + + ! prescribed fire + integer :: rx_fire ! is there a prescribed fire? [1=yes; 0=no] + real(r8) :: rx_fi ! average fire intensity of prescribed fire flaming front + real(r8) :: rx_frac_burnt ! fraction burnt by prescribed fire, it's user defined at patch level per fire event ! fire effects real(r8) :: scorch_ht(maxpft) ! scorch height [m] @@ -269,7 +300,8 @@ subroutine Init(this, num_swb, num_levsoil) allocate(this%sabs_dir(num_swb)) allocate(this%sabs_dif(num_swb)) allocate(this%fragmentation_scaler(num_levsoil)) - + allocate(this%co_scr(max_cohort_per_patch)) + ! initialize all values to nan call this%NanValues() @@ -503,6 +535,12 @@ subroutine NanValues(this) this%tau_l = nan this%fi = nan this%fire = fates_unset_int + this%nonrx_fire = fates_unset_int + this%rx_fire = fates_unset_int + this%nonrx_fi = nan + this%nonrx_frac_burnt = nan + this%rx_fi = nan + this%rx_frac_burnt = nan this%fd = nan this%scorch_ht(:) = nan this%tfc_ros = nan @@ -593,6 +631,10 @@ subroutine ZeroValues(this) this%scorch_ht(:) = 0.0_r8 this%tfc_ros = 0.0_r8 this%frac_burnt = 0.0_r8 + this%nonrx_fi = 0.0_r8 + this%nonrx_frac_burnt = 0.0_r8 + this%rx_fi = 0.0_r8 + this%rx_frac_burnt = 0.0_r8 end subroutine ZeroValues @@ -878,6 +920,7 @@ subroutine FreeMemory(this, regeneration_model, numpft) this%sabs_dir, & this%sabs_dif, & this%fragmentation_scaler, & + this%co_scr, & stat=istat, errmsg=smsg) ! These arrays are allocated via a call from EDCanopyStructureMod @@ -1141,7 +1184,7 @@ end subroutine CountCohorts !=========================================================================== - subroutine SortCohorts(this) + subroutine SortCohorts(this,check_order) ! ! DESCRIPTION: sort cohorts in patch's linked list ! uses insertion sort to build a new list @@ -1149,11 +1192,21 @@ subroutine SortCohorts(this) ! ARGUMENTS: class(fates_patch_type), intent(inout), target :: this ! patch + + logical, optional, intent(in) :: check_order ! LOCALS: type(fates_cohort_type), pointer :: currentCohort type(fates_cohort_type), pointer :: nextCohort + + logical :: check_order_present + if (present(check_order)) then + check_order_present = check_order + else + check_order_present = .false. + end if + ! check for inconsistent list state if (.not. associated(this%shortest) .and. .not. associated(this%tallest)) then ! empty list @@ -1166,6 +1219,23 @@ subroutine SortCohorts(this) ! hold on to current linked list so we don't lose it currentCohort => this%shortest + + if(check_order_present)then + do while (associated(currentCohort)) + if( associated(currentCohort%taller)) then + if(currentCohort%height > currentCohort%taller%height)then + write(fates_log(),*) 'Cohort sort checking has failed,' + write(fates_log(),*) 'they are not in height order:' + write(fates_log(),*) 'current: ',currentCohort%height + write(fates_log(),*) 'taller: ',currentCohort%taller%height + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + end if + currentCohort => currentCohort%taller + end do + return + end if + ! reset the current list: we'll build it incrementally this%shortest => null() diff --git a/biogeophys/CMakeLists.txt b/biogeophys/CMakeLists.txt index b232b22a95..09221e0974 100644 --- a/biogeophys/CMakeLists.txt +++ b/biogeophys/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND fates_sources FatesHydroWTFMod.F90 + LeafBiophysicsMod.F90 FatesPlantHydraulicsMod.F90) sourcelist_to_parent(fates_sources) \ No newline at end of file diff --git a/biogeophys/FatesLeafBiophysParamsMod.F90 b/biogeophys/FatesLeafBiophysParamsMod.F90 index 59973354ca..6cc85e2176 100644 --- a/biogeophys/FatesLeafBiophysParamsMod.F90 +++ b/biogeophys/FatesLeafBiophysParamsMod.F90 @@ -114,6 +114,11 @@ subroutine LeafBiophysRegisterParams(fates_params) call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) + name = 'fates_leaf_fnps' + call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & + dimension_names=dim_names, lower_bounds=dim_lower_bound) + + return end subroutine LeafBiophysRegisterParams @@ -121,21 +126,11 @@ end subroutine LeafBiophysRegisterParams subroutine LeafBiophysReceiveParams(fates_params) - !use FatesInterfaceTypesMod, only : hlm_daylength_factor_switch - !use FatesInterfaceTypesMod, only : hlm_stomatal_model - !use FatesInterfaceTypesMod, only : hlm_stomatal_assim_model - !use FatesInterfaceTypesMod, only : hlm_photo_tempsens_model - class(fates_parameters_type), intent(inout) :: fates_params real(r8), allocatable :: tmpreal(:) ! Temporary variable to hold floats real(r8) :: tmpscalar character(len=param_string_length) :: name - !lb_params%dayl_switch = hlm_daylength_factor_switch - !lb_params%stomatal_model = hlm_stomatal_model - !lb_params%stomatal_assim_model = hlm_stomatal_assim_model - !lb_params%photo_tempsens_model = hlm_photo_tempsens_model - name = 'fates_leaf_c3psn' call fates_params%RetrieveParameterAllocate(name=name, & data=tmpreal) @@ -213,6 +208,9 @@ subroutine LeafBiophysReceiveParams(fates_params) call fates_params%RetrieveParameterAllocate(name=name, & data=lb_params%jmaxse) + name = 'fates_leaf_fnps' + call fates_params%RetrieveParameterAllocate(name=name, & + data=lb_params%fnps) return end subroutine LeafBiophysReceiveParams @@ -240,10 +238,12 @@ subroutine LeafBiophysReportParams(is_master) write(fates_log(),fmt_rout) 'fates_leaf_jmaxhd = ',lb_params%jmaxhd write(fates_log(),fmt_rout) 'fates_leaf_vcmaxse = ',lb_params%vcmaxse write(fates_log(),fmt_rout) 'fates_leaf_jmaxse = ',lb_params%jmaxse - write(fates_log(),fmt_iout) 'fates_daylength_factor_switch = ',lb_params%dayl_switch - write(fates_log(),fmt_iout) 'fates_leaf_stomatal_model = ',lb_params%stomatal_model - write(fates_log(),fmt_iout) 'fates_leaf_stomatal_assim_model = ',lb_params%stomatal_assim_model - write(fates_log(),fmt_iout) 'fates_leaf_photo_tempsens_model = ',lb_params%photo_tempsens_model + write(fates_log(),fmt_rout) 'fates_leaf_fnps = ',lb_params%fnps + write(fates_log(),fmt_rout) 'nl: electron_transport_model = ',lb_params%electron_transport_model + write(fates_log(),fmt_iout) 'nl: daylength_factor_switch = ',lb_params%dayl_switch + write(fates_log(),fmt_iout) 'nl: leaf_stomatal_model = ',lb_params%stomatal_model + write(fates_log(),fmt_iout) 'nl: leaf_stomatal_assim_model = ',lb_params%stomatal_assim_model + write(fates_log(),fmt_iout) 'nl: leaf_photo_tempsens_model = ',lb_params%photo_tempsens_model write(fates_log(),fmt_rout) 'fates_leaf_stomatal_slope_medlyn = ',lb_params%medlyn_slope write(fates_log(),fmt_rout) 'fates_leaf_stomatal_slope_ballberry = ',lb_params%bb_slope write(fates_log(),fmt_rout) 'fates_leaf_stomatal_intercept = ',lb_params%stomatal_intercept diff --git a/biogeophys/FatesPlantHydraulicsMod.F90 b/biogeophys/FatesPlantHydraulicsMod.F90 index 013fbdf9f1..1672ec5ff8 100644 --- a/biogeophys/FatesPlantHydraulicsMod.F90 +++ b/biogeophys/FatesPlantHydraulicsMod.F90 @@ -294,7 +294,7 @@ subroutine hydraulics_drive( nsites, sites, bc_in,bc_out,dtime ) case (1) - call FillDrainRhizShells(nsites, sites, bc_in, bc_out ) + call FillDrainRhizShells(nsites, sites, bc_in) call hydraulics_BC(nsites, sites,bc_in,bc_out,dtime ) case (2) @@ -840,7 +840,7 @@ end subroutine SavePreviousCompartmentVolumes ! ===================================================================================== - subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort,bc_in) + subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort) ! DESCRIPTION: Updates absorbing root length (total and its vertical distribution) @@ -853,7 +853,6 @@ subroutine UpdateSizeDepPlantHydProps(currentSite,ccohort,bc_in) ! ARGUMENTS: type(ed_site_type) , intent(in) :: currentSite ! Site stuff type(fates_cohort_type) , intent(inout) :: ccohort ! current cohort pointer - type(bc_in_type) , intent(in) :: bc_in ! Boundary Conditions ! Locals integer :: nlevrhiz ! Number of total soil layers @@ -1203,14 +1202,13 @@ end function constrain_water_contents ! ===================================================================================== -subroutine FuseCohortHydraulics(currentSite,currentCohort, nextCohort, bc_in, newn) +subroutine FuseCohortHydraulics(currentSite,currentCohort, nextCohort, newn) type(fates_cohort_type), intent(inout), target :: currentCohort ! current cohort type(fates_cohort_type), intent(inout), target :: nextCohort ! next (donor) cohort type(ed_site_type), intent(inout), target :: currentSite ! current site - type(bc_in_type), intent(in) :: bc_in real(r8), intent(in) :: newn ! !LOCAL VARIABLES: @@ -1789,7 +1787,7 @@ subroutine UpdateH2OVeg(csite,bc_out,prev_site_h2o,icall) end subroutine UpdateH2OVeg !===================================================================================== -subroutine RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) +subroutine RecruitWUptake(nsites,sites,dtime,recruitflag) ! ---------------------------------------------------------------------------------- ! This subroutine is called to calculate the water requirement for newly recruited cohorts @@ -1804,7 +1802,6 @@ subroutine RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) ! Arguments integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) - type(bc_in_type), intent(in) :: bc_in(nsites) real(r8), intent(in) :: dtime !time (seconds) logical, intent(out) :: recruitflag !flag to check if there is newly recruited cohorts @@ -2186,7 +2183,7 @@ end subroutine BTranForHLMDiagnosticsFromCohortHydr ! ========================================================================== -subroutine FillDrainRhizShells(nsites, sites, bc_in, bc_out) +subroutine FillDrainRhizShells(nsites, sites, bc_in) ! ! Created by Brad Christoffersen, Jan 2016 ! @@ -2212,7 +2209,6 @@ subroutine FillDrainRhizShells(nsites, sites, bc_in, bc_out) integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) type(bc_in_type), intent(in) :: bc_in(nsites) - type(bc_out_type), intent(inout) :: bc_out(nsites) ! Locals type(ed_site_hydr_type), pointer :: csite_hydr ! pointer to site hydraulics object @@ -2444,7 +2440,7 @@ subroutine hydraulics_bc ( nsites, sites, bc_in, bc_out, dtime) ! ---------------------------------------------------------------------------------- !For newly recruited cohorts, add the water uptake demand to csite_hydr%recruit_w_uptake - call RecruitWUptake(nsites,sites,bc_in,dtime,recruitflag) + call RecruitWUptake(nsites,sites,dtime,recruitflag) !update water storage in veg after incorporating newly recuited cohorts if(recruitflag)then @@ -4314,7 +4310,7 @@ end subroutine AccumulateMortalityWaterStorage !-------------------------------------------------------------------------------! -subroutine RecruitWaterStorage(nsites,sites,bc_out) +subroutine RecruitWaterStorage(nsites,sites) ! --------------------------------------------------------------------------- ! This subroutine accounts for the water bound in plants that have @@ -4329,7 +4325,6 @@ subroutine RecruitWaterStorage(nsites,sites,bc_out) ! Arguments integer, intent(in) :: nsites type(ed_site_type), intent(inout), target :: sites(nsites) - type(bc_out_type), intent(inout) :: bc_out(nsites) ! Locals type(fates_cohort_type), pointer :: currentCohort diff --git a/biogeophys/FatesPlantRespPhotosynthMod.F90 b/biogeophys/FatesPlantRespPhotosynthMod.F90 index b51dde4c37..60cf234a64 100644 --- a/biogeophys/FatesPlantRespPhotosynthMod.F90 +++ b/biogeophys/FatesPlantRespPhotosynthMod.F90 @@ -165,7 +165,7 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) real(r8) :: psn_z(nlevleaf,maxpft,nclmax) ! carbon 13 in newly assimilated carbon at leaf level - real(r8) :: c13disc_z(nlevleaf,maxpft,nclmax) + real(r8) :: c13disc_z(nlevleaf,maxpft,nclmax) ! Mask used to determine which leaf-layer biophysical rates have been ! used already @@ -216,11 +216,8 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) real(r8) :: lnc_top ! Leaf nitrogen content per unit area at canopy top [gN/m2] real(r8) :: lmr25top ! canopy top leaf maint resp rate at 25C ! for this plant or pft (umol CO2/m**2/s) - real(r8) :: leaf_inc ! LAI-only portion of the vegetation increment of dinc_vai real(r8) :: lai_canopy_above ! the LAI in the canopy layers above the layer of interest - real(r8) :: lai_layers_above ! the LAI in the leaf layers, within the current canopy, - ! above the leaf layer of interest - real(r8) :: lai_current ! the LAI in the current leaf layer + real(r8) :: leaf_veg_frac ! fraction of vegetation area (leaf+stem) that is just leaf real(r8) :: cumulative_lai ! the cumulative LAI, top down, to the leaf layer of interest real(r8) :: leaf_psi ! leaf xylem matric potential [MPa] (only meaningful/used w/ hydro) real(r8) :: fnrt_mr_layer ! fine root maintenance respiation per layer [kgC/plant/s] @@ -415,7 +412,7 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) c13disc_z(:,:,:) = 0._r8 rs_z(:,:,:) = 0._r8 lmr_z(:,:,:) = 0._r8 - + if_any_cohorts: if(currentPatch%num_cohorts > 0)then currentCohort => currentPatch%tallest @@ -478,6 +475,9 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) canopy_mask_if: if(currentPatch%canopy_mask(cl,ft) == 1)then ! Loop over leaf-layers + + leaf_veg_frac = currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) + leaf_layer_loop : do iv = 1,currentCohort%nv ! ------------------------------------------------------------ @@ -510,20 +510,18 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) if (hlm_use_planthydro.eq.itrue ) then btran_eff = currentCohort%co_hydr%btran + + ! Find the cumulative LAI from the top of this cohort's crown to the + ! center of the current veg layer. If this is the cohort's last layer + ! then the mid-point is between the dlower and the total lai - ! dinc_vai(:) is the total vegetation area index of each "leaf" layer - ! we convert to the leaf only portion of the increment - ! ------------------------------------------------------ - leaf_inc = dinc_vai(iv) * & - currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) - - ! Now calculate the cumulative top-down lai of the current layer's midpoint - lai_canopy_above = sum(currentPatch%canopy_layer_tlai(1:cl-1)) - - lai_layers_above = (dlower_vai(iv) - dinc_vai(iv)) * & - currentCohort%treelai/(currentCohort%treelai+currentCohort%treesai) - lai_current = min(leaf_inc, currentCohort%treelai - lai_layers_above) - cumulative_lai = lai_canopy_above + lai_layers_above + 0.5*lai_current + lai_canopy_above = sum(currentPatch%canopy_layer_tlai(1:cl-1)) + + if(iv == currentCohort%nv) then + cumulative_lai = lai_canopy_above + leaf_veg_frac * (dlower_vai(iv)+0.5_r8*(currentCohort%treelai+currentCohort%treesai-dlower_vai(iv))) + else + cumulative_lai = lai_canopy_above + leaf_veg_frac * (dlower_vai(iv)+0.5_r8*dinc_vai(iv)) + end if leaf_psi = currentCohort%co_hydr%psi_ag(1) @@ -789,6 +787,7 @@ subroutine FatesPlantRespPhotosynthDrive (nsites, sites,bc_in,bc_out,dtime) psn_z(iv,ft,cl) = psn_z(iv,ft,cl) + area_frac * psn_ll anet_av_z(iv,ft,cl) = anet_av_z(iv,ft,cl) + area_frac * anet_ll c13disc_z(iv,ft,cl) = c13disc_z(iv,ft,cl) + area_frac * c13disc_ll + end do do_sunsha diff --git a/biogeophys/LeafBiophysicsMod.F90 b/biogeophys/LeafBiophysicsMod.F90 index bcae231d3d..c238eebf16 100644 --- a/biogeophys/LeafBiophysicsMod.F90 +++ b/biogeophys/LeafBiophysicsMod.F90 @@ -111,6 +111,10 @@ module LeafBiophysicsMod integer, parameter :: net_assim_model = 1 integer, parameter :: gross_assim_model = 2 + ! Constants defining the electron transport model to use + integer, public, parameter :: FvCB1980 = 1 + integer, public, parameter :: JohnsonBerry2021 = 2 + ! Constants defining the photosynthesis temperature acclimation model integer, parameter :: photosynth_acclim_model_none = 0 integer, parameter :: photosynth_acclim_model_kumarathunge_etal_2019 = 1 @@ -124,9 +128,6 @@ module LeafBiophysicsMod ! These two are public for error checking during parameter read-in real(r8), parameter, public :: lmr_r_1 = 0.2061_r8 ! (umol CO2/m**2/s / (gN/(m2 leaf))) real(r8), parameter, public :: lmr_r_2 = -0.0402_r8 ! (umol CO2/m**2/s/degree C) - - ! Fraction of light absorbed by non-photosynthetic pigments - real(r8),parameter :: fnps = 0.15_r8 ! term accounting that two photons are needed to fully transport a single ! electron in photosystem 2 @@ -209,6 +210,9 @@ module LeafBiophysicsMod ! gross assimilation in the stomata model integer :: stomatal_model ! switch for choosing between stomatal conductance models, ! for Ball-Berry, 2 for Medlyn + integer :: electron_transport_model ! index for electron transport model + ! 1: Farquhar von Caemmerer and Berry (FvCB 1980) + ! 2: Johnson and Berry (2021) integer,allocatable :: stomatal_btran_model(:) ! index for how btran effects conductance ! 0: btran does not scale the stomatal slope or intercept ! 1: btran scales the stomatal intercept only @@ -218,7 +222,7 @@ module LeafBiophysicsMod ! 0: btran does not scale vcmax or jmax ! 1: btran scales only vcmax ! 2: btran scales both vcmax and jmax - + real(r8),allocatable :: fnps(:) ! fraction of light absorbed by non-photosynthetic pigments ! ------------------------------------------------------------------------------------- ! Note the omission of several parameter constants: ! @@ -436,11 +440,38 @@ end function AgrossRubiscoC3 ! ===================================================================================== - function GetJe(par_abs,jmax) result(je) + function GetJe(par_abs,jmax,fnps) result (je) ! Input real(r8) :: par_abs ! Absorbed PAR per leaf area [umol photons/m**2/s] real(r8) :: jmax ! maximum electron transport rate (umol electrons/m**2/s) + real(r8) :: fnps ! Fraction of light absorbed by non-photosynthetic pigments + real(r8) :: je ! electron transport rate (umol electrons/m**2/s) + + select case(lb_params%electron_transport_model) + case (FvCB1980) + ! Get the smoothed (quadratic between J and Jmax) electron transport rate + je = GetJe_FvCB(par_abs,jmax,fnps) + + case (JohnsonBerry2021) + je = GetJe_JB(par_abs,jmax,fnps) + + case default + write (fates_log(),*)'error, incorrect leaf electron transport model specified:',lb_params%electron_transport_model + call endrun(msg=errMsg(sourcefile, __LINE__)) + end select + + end function GetJe + + + !====================================================================================== + + function GetJe_FvCB(par_abs,jmax,fnps) result(je) + + ! Input + real(r8) :: par_abs ! Absorbed PAR per leaf area [umol photons/m**2/s] + real(r8) :: jmax ! maximum electron transport rate (umol electrons/m**2/s) + real(r8) :: fnps ! Fraction of light absorbed by non-photosynthetic pigments real(r8) :: je ! electron transport rate (umol electrons/m**2/s) real(r8) :: aquad,bquad,cquad ! terms for quadratic equations real(r8) :: r1,r2 ! roots of quadratic equation @@ -470,15 +501,51 @@ function GetJe(par_abs,jmax) result(je) je = min(r1,r2) - end function GetJe + end function GetJe_FvCB + + ! ===================================================================================== + + function GetJe_JB(par_abs,jmax,fnps) result(je) + + ! Input + real(r8) :: par_abs ! Absorbed PAR per leaf area [umol photons/m**2/s] + real(r8) :: jmax ! maximum electron transport rate (umol electrons/m**2/s) + real(r8) :: fnps ! Fraction of light absorbed by non-photosynthetic pigments + real(r8) :: je ! electron transport rate (umol electrons/m**2/s) + real(r8) :: phi ! maximum quantum yield (mol electrons/mol photons) + real(r8) :: cb6fmax ! maximum activity of the cytochrome b6f complex + ! (umol electrons/m**2/s) + ! referred to as vqmax in Lamour et al. + real(r8) :: Qsat ! Saturating irradiance - assumed to be a constant (umol/m**2/s) + ! Here we asssume abosorbed irradiance + real(r8) :: jsat ! Electron transport rate estimated by the FvCB model for a + ! given Jmax at Qsat + + Qsat = 1530.0_r8 + + phi = (1.0_r8 - fnps) * photon_to_e + + ! Calculate jsat + jsat = GetJe_FvCB(par_abs, jmax, fnps) + + ! Equation to convert PFT specific Jmax to cb6fmax + cb6fmax = (Qsat * jsat) / & + (Qsat - (jsat / phi) ) + + ! Simplified RH JB formulation + je = par_abs * cb6fmax / & + ( (cb6fmax / phi) + par_abs ) + + end function GetJe_JB ! ===================================================================================== - function AgrossRuBPC3(par_abs,jmax,ci,co2_cpoint) result(aj) + function AgrossRuBPC3(par_abs,jmax,fnps,ci,co2_cpoint) result(aj) ! Input real(r8) :: par_abs ! Absorbed PAR per leaf area [umol photons/m2leaf/s ] real(r8) :: jmax ! maximum electron transport rate (umol electrons/m**2/s) + real(r8) :: fnps ! Fraction of light absorbed by non-photosynthetic pigments real(r8) :: ci ! intracellular leaf CO2 (Pa) real(r8) :: co2_cpoint ! CO2 compensation point (Pa) @@ -487,14 +554,13 @@ function AgrossRuBPC3(par_abs,jmax,ci,co2_cpoint) result(aj) ! locals real(r8) :: je ! actual electron transport rate (umol electrons/m**2/s) - - ! Get the smoothed (quadratic between J and Jmax) electron transport rate - je = GetJe(par_abs,jmax) + je = GetJe(par_abs,jmax,fnps) + aj = je * max(ci-co2_cpoint, 0._r8) / & - (4._r8*ci+8._r8*co2_cpoint) - + (4._r8*ci+8._r8*co2_cpoint) + end function AgrossRuBPC3 @@ -718,8 +784,8 @@ subroutine CiMinMax(ft,vcmax,jmax,kp,co2_cpoint,mm_kco2,mm_ko2, & end if ! Get the maximum e tranport rate for when we solve for RuBP (twice) - Je = GetJe(par_abs,jmax) - + je = GetJe(par_abs,jmax,lb_params%fnps(ft)) + ! Find ci at maximum conductance (1/inf = 0) a = can_co2_ppress @@ -747,7 +813,8 @@ subroutine CiMinMax(ft,vcmax,jmax,kp,co2_cpoint,mm_kco2,mm_ko2, & f = 8._r8*co2_cpoint g = lmr ci(2) = CiFromAnetDiffGrad(a,b,c,d,e,f,g) - ag(2) = AgrossRuBPC3(par_abs,jmax,ci(2),co2_cpoint) + ag(2) = AgrossRuBPC3(par_abs,jmax, & + lb_params%fnps(ft),ci(2),co2_cpoint) if(debug)then if ( abs((can_co2_ppress-ci(2))/b - (ag(2)-lmr)) > 1.e-3_r8 ) then @@ -785,7 +852,7 @@ subroutine CiMinMax(ft,vcmax,jmax,kp,co2_cpoint,mm_kco2,mm_ko2, & f = 8._r8*co2_cpoint g = lmr ci(2) = CiFromAnetDiffGrad(a,b,c,d,e,f,g) - ag(2) = AgrossRuBPC3(par_abs,jmax,ci(2),co2_cpoint) + ag(2) = AgrossRuBPC3(par_abs,jmax,lb_params%fnps(ft),ci(2),co2_cpoint) if(debug)then if ( abs((can_co2_ppress-ci(2))/b -(ag(2)-lmr)) > 1.e-3_r8 ) then @@ -904,7 +971,7 @@ subroutine CiFunc(ci, & ac = AgrossRubiscoC3(vcmax,ci,can_o2_ppress,co2_cpoint,mm_kco2,mm_ko2) ! C3: RuBP-limited photosynthesis - aj = AgrossRuBPC3(par_abs,jmax,ci,co2_cpoint ) + aj = AgrossRuBPC3(par_abs,jmax, lb_params%fnps(ft),ci,co2_cpoint ) ! Take the minimum, no smoothing agross = min(ac,aj) diff --git a/fire/FatesRxFireMod.F90 b/fire/FatesRxFireMod.F90 new file mode 100644 index 0000000000..a025597ba9 --- /dev/null +++ b/fire/FatesRxFireMod.F90 @@ -0,0 +1,51 @@ +module FatesRxFireMod + + + ! ============================================================================ + ! Methods to help with prescribed fire + ! ============================================================================ + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : nearzero + + implicit none + private + + public :: is_prescribed_burn + + contains + + logical function is_prescribed_burn(wildfire_FI, wildfire_ignitions, rx_min_FI, & + rx_max_FI, wildfire_FI_thresh) + ! + ! DESCRIPTION: + ! Determines if a prescribed burn is happening + ! + + ! ARGUMENTS: + real(r8), intent(in) :: wildfire_FI ! wildfire fire intensity [kW/m] + real(r8), intent(in) :: wildfire_ignitions ! wildfire ignitions [count/km2/day] + real(r8), intent(in) :: rx_min_FI ! minimum fire energy of prescribed fire [kW/m] + real(r8), intent(in) :: rx_max_FI ! maximum fire energy of prescribed fire [kW/m] + real(r8), intent(in) :: wildfire_FI_thresh ! threshold for fires that spread or go out [kW/m] + + ! LOCALS: + logical :: rx_man ! prescribed fire using human ignitions + logical :: rx_hyb ! prescribed fire due to both lightning strike and human ignitions + logical :: within_rx_FI_range ! fire intensity is within prescribed burn limits + + ! check if fire intensity falls within prescribed burn range + within_rx_FI_range = wildfire_FI > rx_min_FI .and. wildfire_FI < rx_max_FI + + ! condition for prescribed burn solely due to human ignitions + rx_man = within_rx_FI_range .and. wildfire_ignitions < nearzero + + ! condition for hybrid prescribed burn (low-intensity fire + human ignitions) + rx_hyb = within_rx_FI_range .and. wildfire_FI < wildfire_FI_thresh .and. & + wildfire_ignitions > nearzero + + is_prescribed_burn = rx_man .or. rx_hyb + + end function is_prescribed_burn + +end module FatesRxFireMod diff --git a/fire/SFEquationsMod.F90 b/fire/SFEquationsMod.F90 index 9e58ec1d9a..ab8fcc16c3 100644 --- a/fire/SFEquationsMod.F90 +++ b/fire/SFEquationsMod.F90 @@ -9,6 +9,8 @@ module SFEquationsMod use FatesConstantsMod, only : r8 => fates_r8 use FatesConstantsMod, only : nearzero + use FatesGlobals, only : endrun => fates_endrun + use shr_log_mod, only : errMsg => shr_log_errMsg implicit none private @@ -29,6 +31,17 @@ module SFEquationsMod public :: FireSize public :: AreaBurnt public :: FireIntensity + public :: ScorchHeight + public :: CrownFractionBurnt + public :: BarkThickness + public :: CambialMortality + public :: TotalFireMortality + public :: CrownFireMortality + public :: CriticalResidenceTime + public :: cambial_mort + + ! for error message writing + character(len=*), parameter :: sourcefile = __FILE__ contains @@ -464,4 +477,188 @@ real(r8) function FireIntensity(fuel_consumed, ros) end function FireIntensity + !--------------------------------------------------------------------------------------- + + real(r8) function ScorchHeight(alpha_SH, FI) + ! + ! DESCRIPTION: + ! Calculates scorch height [m] + ! + ! Equation 16 in Thonicke et al. 2010 + ! Van Wagner 1973 Eq. 8; Byram (1959) + ! + + ! ARGUMENTS: + real(r8), intent(in) :: alpha_SH ! alpha parameter for scorch height equation + real(r8), intent(in) :: FI ! fire intensity [kW/m] + + if (FI < nearzero) then + ScorchHeight = 0.0_r8 + else + ScorchHeight = alpha_SH*(FI**0.667_r8) + end if + + end function ScorchHeight + + !--------------------------------------------------------------------------------------- + + real(r8) function CrownFractionBurnt(SH, height, crown_depth) + ! + ! DESCRIPTION: + ! Calculates fraction of the crown burnt of woody plants + ! Equation 17 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: SH ! scorch height [m] + real(r8), intent(in) :: height ! plant height [m] + real(r8), intent(in) :: crown_depth ! crown depth [m] + + if (crown_depth < nearzero) then + CrownFractionBurnt = 0.0_r8 + else + CrownFractionBurnt = (SH - height + crown_depth)/crown_depth + CrownFractionBurnt = min(1.0_r8, max(0.0_r8, CrownFractionBurnt)) + end if + + end function CrownFractionBurnt + + !--------------------------------------------------------------------------------------- + + real(r8) function BarkThickness(bark_scalar, dbh) + ! + ! DESCRIPTION: + ! Calculates bark thickness [cm] + ! Equation 21 in Thonicke et al 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: bark_scalar ! bark per dbh [cm/cm] + real(r8), intent(in) :: dbh ! diameter at breast height [cm] + + BarkThickness = bark_scalar*dbh + + if (BarkThickness < nearzero) then + call endrun(msg="bark thickness is negative", & + additional_msg=errMsg(sourcefile, __LINE__)) + end if + + end function BarkThickness + + !--------------------------------------------------------------------------------------- + + real(r8) function CriticalResidenceTime(bark_thickness) + ! + ! DESCRIPTION: + ! Calculates critical fire residence time for cambial damage [min] + ! Equation 19 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: bark_thickness ! bark thickness [cm] + + CriticalResidenceTime = 2.9_r8*bark_thickness**2.0_r8 + + end function CriticalResidenceTime + + !--------------------------------------------------------------------------------------- + + real(r8) function CambialMortality(bark_scalar, dbh, tau_l) + ! + ! DESCRIPTION: + ! Calculates rate of cambial damage mortality [0-1] + ! Equation 19 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: bark_scalar ! cm bark per cm dbh [cm/cm] + real(r8), intent(in) :: dbh ! diameter at breast height [cm] + real(r8), intent(in) :: tau_l ! residence time of fire [min] + + ! LOCALS: + real(r8) :: bark_thickness ! bark thickness [cm] + real(r8) :: tau_c ! critical fire residence time for cambial damage [min] + real(r8) :: tau_r ! relative fire residence time (actual / critical) + + ! calculate bark thickness based of bark scalar parameter and DBH + bark_thickness = BarkThickness(bark_scalar, dbh) + + ! calculate critical residence time for cambial damage [min] + tau_c = CriticalResidenceTime(bark_thickness) + + ! relative residence time + tau_r = tau_l/tau_c + + CambialMortality = cambial_mort(tau_r) + + end function CambialMortality + + !--------------------------------------------------------------------------------------- + + real(r8) function cambial_mort(tau_r) + ! + ! DESCRIPTION: + ! Helper function for CambialMortality + ! Calculates rate of cambial damage mortality [0-1] + ! Equation 19 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: tau_r ! relative residence time of fire + + if (tau_r >= 2.0_r8) then + cambial_mort = 1.0_r8 + else if (tau_r < 2.0_r8 .and. tau_r > 0.22_r8) then + cambial_mort = 0.563_r8*tau_r - 0.125_r8 + else + cambial_mort = 0.0_r8 + end if + + end function cambial_mort + + !--------------------------------------------------------------------------------------- + + real(r8) function CrownFireMortality(crown_kill, fraction_crown_burned) + ! + ! DESCRIPTION: + ! Calculates rate of mortality from crown scorching [0-1] + ! Equation 19 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: crown_kill ! parameter for crown kill cm bark per cm dbh [cm/cm] + real(r8), intent(in) :: fraction_crown_burned ! fraction of the crown burned [0-1] + + CrownFireMortality = crown_kill*fraction_crown_burned**3.0_r8 + if (CrownFireMortality > 1.0_r8) CrownFireMortality = 1.0_r8 + if (CrownFireMortality < nearzero) CrownFireMortality = 0.0_r8 + + end function CrownFireMortality + + !--------------------------------------------------------------------------------------- + + real(r8) function TotalFireMortality(crownfire_mort, cambial_damage_mort) + ! + ! DESCRIPTION: + ! Calculates rate of mortality from wildfire [0-1] + ! Equation 18 in Thonicke et al. 2010 + ! + + ! ARGUMENTS: + real(r8), intent(in) :: crownfire_mort ! mortality rate from crown scorching [0-1] + real(r8), intent(in) :: cambial_damage_mort ! mortality rate from cambial damage [0-1] + + if (crownfire_mort > 1.0_r8 .or. cambial_damage_mort > 1.0_r8) then + TotalFireMortality = 1.0_r8 + else + TotalFireMortality = crownfire_mort + cambial_damage_mort - (crownfire_mort*cambial_damage_mort) + end if + + if (TotalFireMortality > 1.0_r8) TotalFireMortality = 1.0_r8 + if (TotalFireMortality < nearzero) TotalFireMortality = 0.0_r8 + + end function TotalFireMortality + + !--------------------------------------------------------------------------------------- + end module SFEquationsMod diff --git a/fire/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 3191b460b1..37bc45eed8 100644 --- a/fire/SFFireWeatherMod.F90 +++ b/fire/SFFireWeatherMod.F90 @@ -1,6 +1,7 @@ module SFFireWeatherMod use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : ifalse implicit none private @@ -9,12 +10,14 @@ module SFFireWeatherMod real(r8) :: fire_weather_index ! fire weather index real(r8) :: effective_windspeed ! effective wind speed, corrected for by tree/grass cover [m/min] + integer :: rx_flag ! prescribed fire burn window flag [1=burn window present; 0=no burn window] contains procedure(initialize_fire_weather), public, deferred :: Init procedure(update_fire_weather), public, deferred :: UpdateIndex procedure, public :: UpdateEffectiveWindSpeed + procedure, public :: UpdateRxfireBurnWindow end type fire_weather @@ -67,4 +70,45 @@ subroutine UpdateEffectiveWindSpeed(this, wind_speed, tree_fraction, grass_fract end subroutine UpdateEffectiveWindSpeed -end module SFFireWeatherMod \ No newline at end of file + subroutine UpdateRxfireBurnWindow(this, rxfire_switch, temp_C, rh, wind, temp_up, & + temp_low,rh_up, rh_low, wind_up, wind_low) + + ! ARGUMENTS + class(fire_weather), intent(inout) :: this ! fire weather class + real(r8), intent(in) :: temp_C ! daily averaged temperature [degrees C] + integer, intent(in) :: rxfire_switch ! whether prescribed fire is turned on + real(r8), intent(in) :: rh ! daily relative humidity [%] + real(r8), intent(in) :: wind ! wind speed [m/min] + real(r8), intent(in) :: temp_up ! user defined upper bound for temp when define a burn window + real(r8), intent(in) :: temp_low ! user defined lower bound for temp when define a burn window + real(r8), intent(in) :: rh_up ! user defined upper bound for relative humidity + real(r8), intent(in) :: rh_low ! user defined lower bound for relative humidity + real(r8), intent(in) :: wind_up ! user defined upper bound for wind speed + real(r8), intent(in) :: wind_low ! user defined lower bound for wind speed + + ! LOCAL VARIABLES + real(r8) :: t_check ! intermediate value derived from temp condition check + real(r8) :: rh_check ! intermediate value derived from RH condition check + real(r8) :: ws_check ! intermediate value derived from wind speed condition check + + if (rxfire_switch .eq. ifalse) return + + ! check if ambient temperature, relative humidity, and wind speed + ! are within user defined ranges by comparing current weather + ! condition to the lower and upper bounds defined. when within range, + ! it should result in negative value or zero (at the boundary condition) + ! for each check below + + t_check = (temp_C - temp_low)*(temp_C - temp_up) + rh_check = (rh - rh_low)*(rh - rh_up) + ws_check = (wind - wind_low)*(wind - wind_up) + + if (t_check <= 0.0_r8 .and. rh_check <= 0.0_r8 .and. ws_check <= 0.0_r8) then + this%rx_flag = 1 + else + this%rx_flag = 0 + end if + + end subroutine UpdateRxfireBurnWindow + +end module SFFireWeatherMod diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index a25323b517..7e6de3323b 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -10,6 +10,8 @@ module SFMainMod use FatesConstantsMod, only : pi_const use FatesConstantsMod, only : nocomp_bareground, nearzero use FatesGlobals, only : fates_log + use FatesGlobals , only : endrun => fates_endrun + use shr_log_mod , only : errMsg => shr_log_errMsg use FatesInterfaceTypesMod, only : hlm_masterproc use FatesInterfaceTypesMod, only : hlm_spitfire_mode use FatesInterfaceTypesMod, only : hlm_sf_nofire_def @@ -26,10 +28,7 @@ module SFMainMod use EDtypesMod, only : AREA use FatesLitterMod, only : litter_type use FatesFuelClassesMod, only : num_fuel_classes - use PRTGenericMod, only : leaf_organ use PRTGenericMod, only : carbon12_element - use PRTGenericMod, only : sapw_organ - use PRTGenericMod, only : struct_organ use FatesInterfaceTypesMod, only : numpft use FatesAllometryMod, only : CrownDepth use FatesFuelClassesMod, only : fuel_classes @@ -40,9 +39,6 @@ module SFMainMod public :: DailyFireModel public :: UpdateFuelCharacteristics - integer :: write_SF = ifalse ! for debugging - logical :: debug = .false. ! for debugging - ! ====================================================================================== contains @@ -66,10 +62,8 @@ subroutine DailyFireModel(currentSite, bc_in) call CalculateSurfaceRateOfSpread(currentSite) call CalculateSurfaceFireIntensity(currentSite) call CalculateAreaBurnt(currentSite) - call crown_scorching(currentSite) - call crown_damage(currentSite) - call cambial_damage_kill(currentSite) - call post_fire_mortality(currentSite) + call CalculateRxFireAreaBurnt(currentSite) + call CalculatePostFireMortality(currentSite) end if end subroutine DailyFireModel @@ -79,15 +73,18 @@ end subroutine DailyFireModel subroutine UpdateFireWeather(currentSite, bc_in) ! ! DESCRIPTION: - ! Updates the site's fire weather index and calculates effective windspeed based on + ! Updates the site's fire weather index, burn window for prescribed fire, and calculates effective windspeed based on ! vegetation characteristics ! ! Currently we use tree and grass fraction averaged over whole grid (site) to ! prevent extreme divergence - use FatesConstantsMod, only : tfrz => t_water_freeze_k_1atm - use FatesConstantsMod, only : sec_per_day, sec_per_min - use EDTypesMod, only : CalculateTreeGrassAreaSite + use FatesConstantsMod, only : tfrz => t_water_freeze_k_1atm + use FatesConstantsMod, only : sec_per_day, sec_per_min + use EDTypesMod, only : CalculateTreeGrassAreaSite + use FatesInterfaceTypesMod, only : hlm_use_managed_fire + use SFParamsMod, only : SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, & + SF_val_rxfire_rhlw, SF_val_rxfire_wdup, SF_val_rxfire_wdlw ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -129,13 +126,18 @@ subroutine UpdateFireWeather(currentSite, bc_in) ! update fire weather index call currentSite%fireWeather%UpdateIndex(temp_C, precip, rh, wind) + ! update prescribed fire burn window + call currentSite%fireWeather%UpdateRxfireBurnWindow(hlm_use_managed_fire, temp_C, rh, wind, & + SF_val_rxfire_tpup, SF_val_rxfire_tplw, SF_val_rxfire_rhup, SF_val_rxfire_rhlw, & + SF_val_rxfire_wdup, SF_val_rxfire_wdlw) + ! calculate site-level tree, grass, and bare fraction call CalculateTreeGrassAreaSite(currentSite, tree_fraction, grass_fraction, bare_fraction) ! update effective wind speed call currentSite%fireWeather%UpdateEffectiveWindSpeed(wind*sec_per_min, tree_fraction, & grass_fraction, bare_fraction) - + end subroutine UpdateFireWeather !--------------------------------------------------------------------------------------- @@ -236,7 +238,7 @@ subroutine CalculateIgnitionsandFDI(currentSite, bc_in) ! if the oldest patch is a bareground patch (i.e. nocomp mode is on) use the first vegetated patch ! for the iofp index (i.e. the next younger patch) currentPatch => currentSite%oldest_patch - if(currentPatch%nocomp_pft_label .eq. nocomp_bareground)then + if (currentPatch%nocomp_pft_label == nocomp_bareground)then currentPatch => currentPatch%younger endif iofp = currentPatch%patchno @@ -305,7 +307,7 @@ subroutine CalculateSurfaceRateOfSpread(currentSite) if (beta_op < nearzero) then beta_ratio = 0.0_r8 else - beta_ratio = beta/beta_op + beta_ratio = beta/beta_op end if ! remove mineral content from fuel load per Thonicke 2010 @@ -350,9 +352,15 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! ! DESCRIPTION: ! Calculates surface fireline intensity for each patch of a site + ! Use calculated fire intensity to determine if prescribed fire or + ! wildfire happens ! - use SFEquationsMod, only : FireIntensity - use SFParamsMod, only : SF_val_fire_threshold + + use SFEquationsMod, only : FireIntensity + use SFParamsMod, only : SF_val_fire_threshold + use SFParamsMod, only : SF_val_rxfire_max_threshold, SF_val_rxfire_min_threshold + use SFParamsMod, only : SF_val_rxfire_fuel_max, SF_val_rxfire_fuel_min + use FatesRxFireMod, only : is_prescribed_burn ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -360,6 +368,10 @@ subroutine CalculateSurfaceFireIntensity(currentSite) ! LOCALS: type(fates_patch_type), pointer :: currentPatch ! patch object real(r8) :: fuel_consumed(num_fuel_classes) ! fuel consumed [kgC/m2] + logical :: is_rxfire ! is it a prescribed fire? + logical :: rxfire_fuel_check ! is fuel within thresholds for prescribed burn + logical :: fi_check ! is (potential) fire intensity high enough for fire to actually happen? + logical :: has_ignition ! is ignition greater than zero? currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -376,21 +388,54 @@ subroutine CalculateSurfaceFireIntensity(currentSite) currentPatch%TFC_ROS = sum(fuel_consumed) - fuel_consumed(fuel_classes%trunks()) ! initialize patch parameters to zero - currentPatch%FI = 0.0_r8 - currentPatch%fire = 0 + currentPatch%FI = 0.0_r8 ! either nonrx or rx FI + currentPatch%nonrx_fire = 0 ! only wildfire + currentPatch%rx_fire = 0 ! only rx fire + currentPatch%rx_FI = 0.0_r8 + currentPatch%nonrx_FI = 0.0_r8 + + has_ignition = currentSite%NF > 0.0_r8 - if (currentSite%NF > 0.0_r8) then + if (has_ignition .or. currentSite%fireWeather%rx_flag == itrue) then ! fire intensity [kW/m] currentPatch%FI = FireIntensity(currentPatch%TFC_ROS/0.45_r8, currentPatch%ROS_front/60.0_r8) + fi_check = currentPatch%FI > SF_val_fire_threshold + + ! check if prescribed fire can occur based on fuel load + rxfire_fuel_check = currentPatch%fuel%non_trunk_loading > SF_val_rxfire_fuel_min .and. & + currentPatch%fuel%non_trunk_loading < SF_val_rxfire_fuel_max + + if (currentSite%fireWeather%rx_flag == itrue .and. rxfire_fuel_check) then + + ! record burnable area after fuel load check + currentSite%rxfire_area_fuel = currentSite%rxfire_area_fuel + currentPatch%area + + ! determine fire type + ! prescribed fire and wildfire cannot happen on the same patch + is_rxfire = is_prescribed_burn(currentPatch%FI, currentSite%NF, & + SF_val_rxfire_min_threshold, SF_val_rxfire_max_threshold, SF_val_fire_threshold) + + if (is_rxfire) then + currentSite%rxfire_area_fi = currentSite%rxfire_area_fi + currentPatch%area ! record burnable area after FI check + currentPatch%rx_fire = 1 + + else if (has_ignition .and. fi_check) then ! (potential) intensity is greater than kW/m energy threshold + currentPatch%nonrx_fire = 1 + end if + + else if (has_ignition .and. fi_check) then ! not a patch suitable for conducting prescribed fire or rxfire is not even turned on, but (potential) intensity is greater than kW/m energy threshold + currentPatch%nonrx_fire = 1 + end if - ! track fires greater than kW/m energy threshold - if (currentPatch%FI > SF_val_fire_threshold) then - currentPatch%fire = 1 + ! assign fire intensities and ignitions based on fire type + if (currentPatch%nonrx_fire == itrue) then currentSite%NF_successful = currentSite%NF_successful + & - currentSite%NF * currentSite%FDI*currentPatch%area / area + currentSite%NF*currentSite%FDI*currentPatch%area/area + currentPatch%nonrx_FI = currentPatch%FI + else if (currentPatch%rx_fire == itrue) then + currentPatch%rx_FI = currentPatch%FI end if - end if end if currentPatch => currentPatch%younger @@ -408,7 +453,6 @@ subroutine CalculateAreaBurnt(currentSite) use FatesConstantsMod, only : m2_per_km2 use SFEquationsMod, only : FireDuration, LengthToBreadth use SFEquationsMod, only : AreaBurnt, FireSize - use SFParamsMod, only : SF_val_fire_threshold ! ARGUMENTS: type(ed_site_type), intent(inout), target :: currentSite @@ -430,9 +474,9 @@ subroutine CalculateAreaBurnt(currentSite) ! initialize patch parameters to zero currentPatch%FD = 0.0_r8 - currentPatch%frac_burnt = 0.0_r8 + currentPatch%nonrx_frac_burnt = 0.0_r8 - if (currentSite%NF > 0.0_r8 .and. currentPatch%FI > SF_val_fire_threshold) then + if (currentPatch%nonrx_fire == 1) then ! fire duration [min] currentPatch%FD = FireDuration(currentSite%FDI) @@ -450,7 +494,7 @@ subroutine CalculateAreaBurnt(currentSite) ! convert to area burned per area patch per day ! i.e., fraction of the patch burned on that day - currentPatch%frac_burnt = min(max_frac_burnt, area_burnt/m2_per_km2) + currentPatch%nonrx_frac_burnt = min(max_frac_burnt, area_burnt/m2_per_km2) end if end if @@ -460,238 +504,140 @@ subroutine CalculateAreaBurnt(currentSite) end subroutine CalculateAreaBurnt !--------------------------------------------------------------------------------------- - - !***************************************************************** - subroutine crown_scorching ( currentSite ) - !***************************************************************** - - !currentPatch%FI average fire intensity of flaming front during day. kW/m. - !currentPatch%SH(pft) scorch height for all cohorts of a given PFT on a given patch (m) - - type(ed_site_type), intent(in), target :: currentSite - - type(fates_patch_type), pointer :: currentPatch - type(fates_cohort_type), pointer :: currentCohort - - real(r8) :: tree_ag_biomass ! total amount of above-ground tree biomass in patch. kgC/m2 - real(r8) :: leaf_c ! leaf carbon [kg] - real(r8) :: sapw_c ! sapwood carbon [kg] - real(r8) :: struct_c ! structure carbon [kg] - - integer :: i_pft - - - currentPatch => currentSite%oldest_patch; - do while(associated(currentPatch)) - - if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - - tree_ag_biomass = 0.0_r8 - if (currentPatch%fire == 1) then - currentCohort => currentPatch%tallest; - do while(associated(currentCohort)) - if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only - - leaf_c = currentCohort%prt%GetState(leaf_organ, carbon12_element) - sapw_c = currentCohort%prt%GetState(sapw_organ, carbon12_element) - struct_c = currentCohort%prt%GetState(struct_organ, carbon12_element) - - tree_ag_biomass = tree_ag_biomass + & - currentCohort%n * (leaf_c + & - prt_params%allom_agb_frac(currentCohort%pft)*(sapw_c + struct_c)) - endif !trees only - currentCohort=>currentCohort%shorter; - enddo !end cohort loop - - do i_pft=1,numpft - if (tree_ag_biomass > 0.0_r8 .and. prt_params%woody(i_pft) == itrue) then - - !Equation 16 in Thonicke et al. 2010 !Van Wagner 1973 EQ8 !2/3 Byram (1959) - currentPatch%Scorch_ht(i_pft) = EDPftvarcon_inst%fire_alpha_SH(i_pft) * (currentPatch%FI**0.667_r8) - - if(write_SF == itrue)then - if ( hlm_masterproc == itrue ) write(fates_log(),*) 'currentPatch%SH',currentPatch%Scorch_ht(i_pft) - endif - else - currentPatch%Scorch_ht(i_pft) = 0.0_r8 - endif ! tree biomass - end do - endif !fire - endif !nocomp_pft_label - - currentPatch => currentPatch%younger; - enddo !end patch loop - - end subroutine crown_scorching + subroutine CalculateRxFireAreaBurnt (currentSite) + ! + ! DESCRIPTION: + ! Returns burned fraction for prescribed fire per patch by first checking + ! if total burnable fraction at site level is greater than user defined fraction of site area + ! if yes, calculate burned fraction as (user defined frac / total burnable frac) + ! + use SFParamsMod, only : SF_val_rxfire_AB ! user defined prescribed fire area in fraction per day to reflect burning capacity + use SFParamsMod, only : SF_val_rxfire_min_frac ! minimum fraction of land needs to be burnable for conducting prescribed fire - !***************************************************************** - subroutine crown_damage ( currentSite ) - !***************************************************************** + ! ARGUMENTS + type(ed_site_type), intent(inout), target :: currentSite - !returns the updated currentCohort%fraction_crown_burned for each tree cohort within each patch. - !currentCohort%fraction_crown_burned is the proportion of crown affected by fire + ! LOCALS + type(fates_patch_type), pointer :: currentPatch + real(r8) :: total_burnable_frac ! total fractional land area that can apply prescribed fire after condition checks at site level - type(ed_site_type), intent(in), target :: currentSite + ! initialize site variables + currentSite%rxfire_area_final = 0.0_r8 + total_burnable_frac = 0.0_r8 - type(fates_patch_type) , pointer :: currentPatch - type(fates_cohort_type), pointer :: currentCohort - real(r8) :: crown_depth ! Depth of crown in meters - + ! update total burnable fraction + total_burnable_frac = currentSite%rxfire_area_fi/AREA + currentPatch => currentSite%oldest_patch - do while(associated(currentPatch)) - - if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - if (currentPatch%fire == 1) then - - currentCohort=>currentPatch%tallest - - do while(associated(currentCohort)) - currentCohort%fraction_crown_burned = 0.0_r8 - if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only - ! Flames lower than bottom of canopy. - ! c%height is height of cohort - - call CrownDepth(currentCohort%height,currentCohort%pft,crown_depth) - - if (currentPatch%Scorch_ht(currentCohort%pft) < & - (currentCohort%height-crown_depth)) then - currentCohort%fraction_crown_burned = 0.0_r8 - else - ! Flames part of way up canopy. - ! Equation 17 in Thonicke et al. 2010. - ! flames over bottom of canopy but not over top. - if ((currentCohort%height > 0.0_r8).and.(currentPatch%Scorch_ht(currentCohort%pft) >= & - (currentCohort%height-crown_depth))) then - - currentCohort%fraction_crown_burned = (currentPatch%Scorch_ht(currentCohort%pft) - & - (currentCohort%height - crown_depth))/crown_depth - - else - ! Flames over top of canopy. - currentCohort%fraction_crown_burned = 1.0_r8 - endif - - endif - ! Check for strange values. - currentCohort%fraction_crown_burned = min(1.0_r8, max(0.0_r8,currentCohort%fraction_crown_burned)) - endif !trees only - !shrink canopy to account for burnt section. - !currentCohort%canopy_trim = min(currentCohort%canopy_trim,(1.0_r8-currentCohort%fraction_crown_burned)) - - currentCohort => currentCohort%shorter; - - enddo !end cohort loop - endif !fire? - endif !nocomp_pft_label check - - currentPatch => currentPatch%younger; - - enddo !end patch loop - - end subroutine crown_damage - - !***************************************************************** - subroutine cambial_damage_kill ( currentSite ) - !***************************************************************** - ! routine description. - ! returns the probability that trees dies due to cambial char - ! currentPatch%tau_l = duration of lethal stem heating (min). Calculated at patch level. - - type(ed_site_type), intent(in), target :: currentSite - - type(fates_patch_type) , pointer :: currentPatch - type(fates_cohort_type), pointer :: currentCohort - - real(r8) :: tau_c !critical time taken to kill cambium (minutes) - real(r8) :: bt !bark thickness in cm. - - currentPatch => currentSite%oldest_patch; - - do while(associated(currentPatch)) - - if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - - if (currentPatch%fire == 1) then - currentCohort => currentPatch%tallest; - do while(associated(currentCohort)) - if ( prt_params%woody(currentCohort%pft) == itrue) then !trees only - ! Equation 21 in Thonicke et al 2010 - bt = EDPftvarcon_inst%bark_scaler(currentCohort%pft)*currentCohort%dbh ! bark thickness. - ! Equation 20 in Thonicke et al. 2010. - tau_c = 2.9_r8*bt**2.0_r8 !calculate time it takes to kill cambium (min) - ! Equation 19 in Thonicke et al. 2010 - if ((currentPatch%tau_l/tau_c) >= 2.0_r8) then - currentCohort%cambial_mort = 1.0_r8 - else - if ((currentPatch%tau_l/tau_c) > 0.22_r8) then - currentCohort%cambial_mort = (0.563_r8*(currentPatch%tau_l/tau_c)) - 0.125_r8 - else - currentCohort%cambial_mort = 0.0_r8 - endif - endif - endif !trees - - currentCohort => currentCohort%shorter; - - enddo !end cohort loop - endif !fire? - endif !nocomp_pft_label check - - currentPatch=>currentPatch%younger; - - enddo !end patch loop - - end subroutine cambial_damage_kill - - !***************************************************************** - subroutine post_fire_mortality ( currentSite ) - !***************************************************************** - - ! returns the updated currentCohort%fire_mort value for each tree cohort within each patch. - ! currentCohort%fraction_crown_burned is proportion of crown affected by fire - ! currentCohort%crownfire_mort probability of tree post-fire mortality due to crown scorch - ! currentCohort%cambial_mort probability of tree post-fire mortality due to cambial char - ! currentCohort%fire_mort post-fire mortality from cambial and crown damage assuming two are independent. - - type(ed_site_type), intent(in), target :: currentSite + do while (associated(currentPatch)) + if (currentPatch%nocomp_pft_label /= nocomp_bareground) then + currentPatch%fire = 0 ! fire, either rx or non-rx + currentPatch%frac_burnt = 0.0_r8 ! rx_frac_burnt + nonrx_frac_burnt + currentPatch%rx_frac_burnt = 0.0_r8 + if (currentPatch%rx_fire == itrue .and. & + total_burnable_frac >= SF_val_rxfire_min_frac ) then + currentSite%rxfire_area_final = currentSite%rxfire_area_final + currentPatch%area ! the final burned total land area + currentPatch%rx_frac_burnt = min(0.99_r8, SF_val_rxfire_AB/total_burnable_frac) + else + currentPatch%rx_fire = 0 ! update rxfire occurence at patch + currentPatch%rx_FI = 0.0_r8 + end if + + ! update patch level fire occurence and total frac burnt + currentPatch%fire = currentPatch%nonrx_fire + currentPatch%rx_fire + currentPatch%frac_burnt = currentPatch%nonrx_frac_burnt + currentPatch%rx_frac_burnt + + ! currentPatch%fire cannot be >1, which indicates both rx and wildfire are happening + ! we currently do not allow this to happen on the same patch yet + if (currentPatch%fire > 1) then + write(fates_log(),*) 'Both wildfire and management fire are happening at same patch' + write(fates_log(),*) 'rxfire =', currentPatch%rx_fire + write(fates_log(),*) 'wildfire =', currentPatch%nonrx_fire + call endrun(msg=errMsg(__FILE__, __LINE__)) + end if + end if + currentPatch => currentPatch%younger + end do - type(fates_patch_type), pointer :: currentPatch - type(fates_cohort_type), pointer :: currentCohort + end subroutine CalculateRxFireAreaBurnt + + !--------------------------------------------------------------------------------------- + subroutine CalculatePostFireMortality(currentSite) + ! + ! DESCRIPTION: + ! Calculates mortality (for woody PFTs) due to fire from crown scorching and cambial damage + ! + use SFEquationsMod, only : ScorchHeight, CrownFireMortality, CrownFractionBurnt + use SFEquationsMod, only : CambialMortality, TotalFireMortality + + ! ARGUMENTS: + type(ed_site_type), intent(in), target :: currentSite ! site object + + ! LOCALS: + type(fates_patch_type), pointer :: currentPatch ! patch object + type(fates_cohort_type), pointer :: currentCohort ! cohort object + real(r8) :: crown_depth ! crown depth [m] + integer :: i_pft ! looping index + currentPatch => currentSite%oldest_patch - - do while(associated(currentPatch)) - - if(currentPatch%nocomp_pft_label .ne. nocomp_bareground)then - - if (currentPatch%fire == 1) then + do while (associated(currentPatch)) + if (currentPatch%nocomp_pft_label /= nocomp_bareground) then + if (currentPatch%fire == 1) then + + ! calculate scorch height [m] + do i_pft = 1, numpft + if (prt_params%woody(i_pft) == itrue) then + currentPatch%Scorch_ht(i_pft) = ScorchHeight(EDPftvarcon_inst%fire_alpha_SH(i_pft), & + currentPatch%FI) + else + currentPatch%Scorch_ht(i_pft) = 0.0_r8 + end if + end do + + ! calculate fire-related mortality currentCohort => currentPatch%tallest - do while(associated(currentCohort)) - currentCohort%fire_mort = 0.0_r8 - currentCohort%crownfire_mort = 0.0_r8 - if ( prt_params%woody(currentCohort%pft) == itrue) then - ! Equation 22 in Thonicke et al. 2010. - currentCohort%crownfire_mort = EDPftvarcon_inst%crown_kill(currentCohort%pft)*currentCohort%fraction_crown_burned**3.0_r8 - ! Equation 18 in Thonicke et al. 2010. - currentCohort%fire_mort = max(0._r8,min(1.0_r8,currentCohort%crownfire_mort+currentCohort%cambial_mort- & - (currentCohort%crownfire_mort*currentCohort%cambial_mort))) !joint prob. - else - currentCohort%fire_mort = 0.0_r8 !Set to zero. Grass mode of death is removal of leaves. - endif !trees - - currentCohort => currentCohort%shorter - - enddo !end cohort loop - endif !fire? - endif !nocomp_pft_label check - - currentPatch => currentPatch%younger - - enddo !end patch loop - - end subroutine post_fire_mortality - - ! ============================================================================ + do while (associated(currentCohort)) + + currentCohort%fraction_crown_burned = 0.0_r8 + currentCohort%fire_mort = 0.0_r8 + currentCohort%crownfire_mort = 0.0_r8 + currentCohort%cambial_mort = 0.0_r8 + + if (prt_params%woody(currentCohort%pft) == itrue) then + + ! calculate crown fraction burned [0-1] + call CrownDepth(currentCohort%height, currentCohort%pft, crown_depth) + currentCohort%fraction_crown_burned = CrownFractionBurnt(currentPatch%Scorch_ht(currentCohort%pft), & + currentCohort%height, crown_depth) + + ! shrink canopy to account for burnt section + ! currentCohort%canopy_trim = min(currentCohort%canopy_trim, 1.0_r8 - currentCohort%fraction_crown_burned) + + ! calculate cambial mortality rate [0-1] + currentCohort%cambial_mort = CambialMortality(EDPftvarcon_inst%bark_scaler(currentCohort%pft), & + currentCohort%dbh, currentPatch%tau_l) + + ! calculate crown fire mortality [0-1] + currentCohort%crownfire_mort = CrownFireMortality(EDPftvarcon_inst%crown_kill(currentCohort%pft), & + currentCohort%fraction_crown_burned) + + ! total fire mortality [0-1] + currentCohort%fire_mort = TotalFireMortality(currentCohort%crownfire_mort, & + currentCohort%cambial_mort) + + end if + currentCohort => currentCohort%shorter + end do + end if + end if + currentPatch => currentPatch%younger + end do + + end subroutine CalculatePostFireMortality + + !--------------------------------------------------------------------------------------- + end module SFMainMod diff --git a/fire/SFNesterovMod.F90 b/fire/SFNesterovMod.F90 index 23128c880f..2a0147058b 100644 --- a/fire/SFNesterovMod.F90 +++ b/fire/SFNesterovMod.F90 @@ -33,6 +33,7 @@ subroutine init_nesterov_fire_weather(this) ! initialize values to 0.0 this%fire_weather_index = 0.0_r8 this%effective_windspeed = 0.0_r8 + this%rx_flag = 0 end subroutine init_nesterov_fire_weather diff --git a/fire/SFParamsMod.F90 b/fire/SFParamsMod.F90 index 65d87e5c6d..85f9f53ea3 100644 --- a/fire/SFParamsMod.F90 +++ b/fire/SFParamsMod.F90 @@ -37,6 +37,19 @@ module SFParamsMod real(r8),protected, public :: SF_val_low_moisture_Slope(num_fuel_classes) real(r8),protected, public :: SF_val_mid_moisture_Coeff(num_fuel_classes) real(r8),protected, public :: SF_val_mid_moisture_Slope(num_fuel_classes) + ! Prescribed fire relevant parameters + real(r8),protected, public :: SF_val_rxfire_tpup ! temperature upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_tplw ! temperature lower threshold below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_rhup ! relative humidity upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_rhlw ! relative humidity lower threshold below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_wdup ! wind speed upper threshold above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_wdlw ! wind speed lower threshold below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_AB ! prescribed fire burned fraction per day + real(r8),protected, public :: SF_val_rxfire_min_threshold ! minimum fire energy at or below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_max_threshold ! maximum fire energy at or above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_fuel_min ! minimum fuel load at or below which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_fuel_max ! maximum fuel load at or above which rx fire is disallowed + real(r8),protected, public :: SF_val_rxfire_min_frac ! minimum burnable fraction at site level at or above which rx fire is allowed character(len=param_string_length),parameter :: SF_name_fdi_alpha = "fates_fire_fdi_alpha" character(len=param_string_length),parameter :: SF_name_miner_total = "fates_fire_miner_total" @@ -57,6 +70,20 @@ module SFParamsMod character(len=param_string_length),parameter :: SF_name_low_moisture_Slope = "fates_fire_low_moisture_Slope" character(len=param_string_length),parameter :: SF_name_mid_moisture_Coeff = "fates_fire_mid_moisture_Coeff" character(len=param_string_length),parameter :: SF_name_mid_moisture_Slope = "fates_fire_mid_moisture_Slope" + character(len=param_string_length),parameter :: SF_name_rxfire_tpup = "fates_rxfire_temp_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_tplw = "fates_rxfire_temp_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_rhup = "fates_rxfire_rh_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_rhlw = "fates_rxfire_rh_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_wdup = "fates_rxfire_wind_upthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_wdlw = "fates_rxfire_wind_lwthreshold" + character(len=param_string_length),parameter :: SF_name_rxfire_AB = "fates_rxfire_AB" + character(len=param_string_length),parameter :: SF_name_rxfire_min_threshold = "fates_rxfire_min_threshold" + character(len=param_string_length),parameter :: SF_name_rxfire_max_threshold = "fates_rxfire_max_threshold" + character(len=param_string_length),parameter :: SF_name_rxfire_fuel_min = "fates_rxfire_fuel_min" + character(len=param_string_length),parameter :: SF_name_rxfire_fuel_max = "fates_rxfire_fuel_max" + character(len=param_string_length),parameter :: SF_name_rxfire_min_frac = "fates_rxfire_min_frac" + + character(len=*), parameter, private :: sourcefile = __FILE__ real(r8), parameter, private :: min_fire_threshold = 0.0001_r8 ! The minimum reasonable fire intensity threshold [kW/m] @@ -157,6 +184,18 @@ subroutine SpitFireParamsInit() SF_val_low_moisture_Slope(:) = nan SF_val_mid_moisture_Coeff(:) = nan SF_val_mid_moisture_Slope(:) = nan + SF_val_rxfire_tpup = nan + SF_val_rxfire_tplw = nan + SF_val_rxfire_rhup = nan + SF_val_rxfire_rhlw = nan + SF_val_rxfire_wdup = nan + SF_val_rxfire_wdlw = nan + SF_val_rxfire_AB = nan + SF_val_rxfire_min_threshold = nan + SF_val_rxfire_max_threshold = nan + SF_val_rxfire_fuel_min = nan + SF_val_rxfire_fuel_max = nan + SF_val_rxfire_min_frac = nan end subroutine SpitFireParamsInit @@ -228,6 +267,45 @@ subroutine SpitFireRegisterScalars(fates_params) call fates_params%RegisterParameter(name=SF_name_fire_threshold, dimension_shape=dimension_shape_scalar, & dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_tpup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_tplw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_rhup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_rhlw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_wdup, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_wdlw, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_AB, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_min_threshold, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_max_threshold, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_fuel_min, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_fuel_max, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + call fates_params%RegisterParameter(name=SF_name_rxfire_min_frac, dimension_shape=dimension_shape_scalar, & + dimension_names=dim_names_scalar) + + + end subroutine SpitFireRegisterScalars @@ -267,6 +345,42 @@ subroutine SpitFireReceiveScalars(fates_params) call fates_params%RetrieveParameter(name=SF_name_fire_threshold, & data=SF_val_fire_threshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_tpup, & + data=SF_val_rxfire_tpup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_tplw, & + data=SF_val_rxfire_tplw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_rhup, & + data=SF_val_rxfire_rhup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_rhlw, & + data=SF_val_rxfire_rhlw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_wdup, & + data=SF_val_rxfire_wdup) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_wdlw, & + data=SF_val_rxfire_wdlw) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_AB, & + data=SF_val_rxfire_AB) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_min_threshold, & + data=SF_val_rxfire_min_threshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_max_threshold, & + data=SF_val_rxfire_max_threshold) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_min, & + data=SF_val_rxfire_fuel_min) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_fuel_max, & + data=SF_val_rxfire_fuel_max) + + call fates_params%RetrieveParameter(name=SF_name_rxfire_min_frac, & + data=SF_val_rxfire_min_frac) diff --git a/functional_unit_testing/parteh/PartehDriver.py b/functional_unit_testing/parteh/PartehDriver.py index 88ca296255..c8319865ae 100644 --- a/functional_unit_testing/parteh/PartehDriver.py +++ b/functional_unit_testing/parteh/PartehDriver.py @@ -176,35 +176,9 @@ def main(): for iplnt in range(num_plants): ipft = use_pfts[iplnt] - evergreen = np.int(fates_params['evergreen'].data[ipft]) - cold_deciduous = np.int(fates_params['season_decid'].data[ipft]) - stress_deciduous = np.int(fates_params['stress_decid'].data[ipft]) - if(evergreen==1): - if(cold_deciduous==1): - print("Poorly defined phenology mode 0") - exit(2) - if(stress_deciduous==1): - print("Poorly defined phenology mode 1") - exit(2) - phen_type.append(1) - elif(cold_deciduous==1): - if(evergreen==1): - print("Poorly defined phenology mode 2") - exit(2) - if(stress_deciduous==1): - print("Poorly defined phenology mode 3") - exit(2) - phen_type.append(2) - elif(stress_deciduous==1): - if(evergreen==1): - print("Poorly defined phenology mode 4") - exit(2) - if(cold_deciduous==1): - print("Poorly defined phenology mode 5") - exit(2) - phen_type.append(3) - else: - print("Unknown phenology mode ? {} {} {}".format(evergreen,cold_deciduous,stress_deciduous)) + phen_leaf_habit = np.int(fates_params['phen_leaf_habit'].data[ipft]) + if(phen_leaf_habit < 1 or phen_leaf_habit > 4): + print("Unknown phenology mode ? {}".format(phen_leaf_habit)) exit(2) diff --git a/functional_unit_testing/parteh/parteh_controls_phenevents_v2.xml b/functional_unit_testing/parteh/parteh_controls_phenevents_v2.xml index 18cf824c62..9d6d25f6eb 100644 --- a/functional_unit_testing/parteh/parteh_controls_phenevents_v2.xml +++ b/functional_unit_testing/parteh/parteh_controls_phenevents_v2.xml @@ -57,9 +57,7 @@ 1 , 1 , 2 , 2 , 2 - 1 , 0 , 1 , 0 , 0 - 0 , 1 , 0 , 1 , 1 - 0 , 0 , 0 , 0 , 0 + 1 , 2 , 1 , 2 , 2 0.2 , 0.2 , 0.2 , 0.2 , 0.2 0.2 , 0.2, 0.2, 0.2, 0.2 30.0 , 30.0 , 30.0, 30.0 , 30.0 diff --git a/functional_unit_testing/parteh/parteh_controls_smoketests.xml b/functional_unit_testing/parteh/parteh_controls_smoketests.xml index d7675c7276..662be047d1 100644 --- a/functional_unit_testing/parteh/parteh_controls_smoketests.xml +++ b/functional_unit_testing/parteh/parteh_controls_smoketests.xml @@ -57,9 +57,7 @@ 1 , 2 , 2 , 2 , 2 - 1 , 1 , 1 , 1 , 1 - 0 , 0 , 0 , 0 , 0 - 0 , 0 , 0 , 0 , 0 + 1 , 1 , 1 , 1 , 1 0.2 , 0.2 , 0.2 , 0.2 , 0.2 0.2 , 0.2, 0.2, 0.2, 0.2 30.0 , 30.0 , 30.0, 30.0 , 30.0 diff --git a/functional_unit_testing/parteh/parteh_controls_variable_netc.xml b/functional_unit_testing/parteh/parteh_controls_variable_netc.xml index 47be70426d..156ef5e47d 100644 --- a/functional_unit_testing/parteh/parteh_controls_variable_netc.xml +++ b/functional_unit_testing/parteh/parteh_controls_variable_netc.xml @@ -55,7 +55,7 @@ 1 , 2 , 2 - 1 , 1, 1 + 1 , 1, 1 0.2 , 0.2, 0.2 0.2 , 0.2 , 0.2 30.0 , 30.0 , 30.0 diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 625e65fabc..09db71632c 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -41,6 +41,8 @@ module EDInitMod use EDTypesMod , only : init_spread_inventory use FatesConstantsMod , only : leaves_on use FatesConstantsMod , only : leaves_off + use FatesConstantsMod , only : ievergreen + use FatesConstantsMod , only : ihard_season_decid use FatesConstantsMod , only : ihard_stress_decid use FatesConstantsMod , only : isemi_stress_decid use PRTGenericMod , only : num_elements @@ -150,6 +152,14 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_ustory(1:nlevsclass,1:numpft)) allocate(site_in%fmort_rate_cambial(1:nlevsclass,1:numpft)) allocate(site_in%fmort_rate_crown(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_rate_crown(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_canopy(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_ustory(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_cambial(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_rate_crown(nlevsclass,1:numpft)) allocate(site_in%growthflux_fusion(1:nlevsclass,1:numpft)) allocate(site_in%mass_balance(1:num_elements)) allocate(site_in%iflux_balance(1:num_elements)) @@ -164,7 +174,15 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) allocate(site_in%fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) allocate(site_in%fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) - allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%nonrx_fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%nonrx_fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%nonrx_fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%nonrx_fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rx_fmort_rate_canopy_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rx_fmort_rate_ustory_damage(1:nlevdamage, 1:nlevsclass, 1:numpft)) + allocate(site_in%rx_fmort_cflux_canopy_damage(1:nlevdamage, 1:nlevsclass)) + allocate(site_in%rx_fmort_cflux_ustory_damage(1:nlevdamage, 1:nlevsclass)) else allocate(site_in%term_nindivs_canopy_damage(1,1,1)) allocate(site_in%term_nindivs_ustory_damage(1,1,1)) @@ -176,6 +194,14 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%fmort_rate_ustory_damage(1,1,1)) allocate(site_in%fmort_cflux_canopy_damage(1,1)) allocate(site_in%fmort_cflux_ustory_damage(1,1)) + allocate(site_in%nonrx_fmort_rate_canopy_damage(1,1,1)) + allocate(site_in%nonrx_fmort_rate_ustory_damage(1,1,1)) + allocate(site_in%nonrx_fmort_cflux_canopy_damage(1,1)) + allocate(site_in%nonrx_fmort_cflux_ustory_damage(1,1)) + allocate(site_in%rx_fmort_rate_canopy_damage(1,1,1)) + allocate(site_in%rx_fmort_rate_ustory_damage(1,1,1)) + allocate(site_in%rx_fmort_cflux_canopy_damage(1,1)) + allocate(site_in%rx_fmort_cflux_ustory_damage(1,1)) end if allocate(site_in%term_carbonflux_canopy(1:n_term_mort_types,1:numpft)) @@ -183,10 +209,17 @@ subroutine init_site_vars( site_in, bc_in, bc_out ) allocate(site_in%imort_carbonflux(1:numpft)) allocate(site_in%fmort_carbonflux_canopy(1:numpft)) allocate(site_in%fmort_carbonflux_ustory(1:numpft)) + allocate(site_in%nonrx_fmort_carbonflux_canopy(1:numpft)) + allocate(site_in%nonrx_fmort_carbonflux_ustory(1:numpft)) + allocate(site_in%rx_fmort_carbonflux_canopy(1:numpft)) + allocate(site_in%rx_fmort_carbonflux_ustory(1:numpft)) allocate(site_in%term_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%imort_abg_flux(1:nlevsclass,1:numpft)) allocate(site_in%fmort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%nonrx_fmort_abg_flux(1:nlevsclass,1:numpft)) + allocate(site_in%rx_fmort_abg_flux(1:nlevsclass,1:numpft)) + site_in%nlevsoil = bc_in%nlevsoil allocate(site_in%rootfrac_scr(site_in%nlevsoil)) @@ -316,6 +349,10 @@ subroutine zero_site( site_in ) site_in%imort_crownarea = 0._r8 site_in%fmort_crownarea_canopy = 0._r8 site_in%fmort_crownarea_ustory = 0._r8 + site_in%nonrx_fmort_crownarea_canopy = 0._r8 + site_in%nonrx_fmort_crownarea_ustory = 0._r8 + site_in%rx_fmort_crownarea_canopy = 0._r8 + site_in%rx_fmort_crownarea_ustory = 0._r8 site_in%term_carbonflux_canopy(:,:) = 0._r8 site_in%term_carbonflux_ustory(:,:) = 0._r8 site_in%recruitment_rate(:) = 0._r8 @@ -327,9 +364,23 @@ subroutine zero_site( site_in ) site_in%fmort_carbonflux_ustory(:) = 0._r8 site_in%fmort_rate_cambial(:,:) = 0._r8 site_in%fmort_rate_crown(:,:) = 0._r8 + site_in%nonrx_fmort_rate_canopy(:,:) = 0._r8 + site_in%nonrx_fmort_rate_ustory(:,:) = 0._r8 + site_in%nonrx_fmort_carbonflux_canopy(:) = 0._r8 + site_in%nonrx_fmort_carbonflux_ustory(:) = 0._r8 + site_in%nonrx_fmort_rate_cambial(:,:) = 0._r8 + site_in%nonrx_fmort_rate_crown(:,:) = 0._r8 + site_in%rx_fmort_rate_canopy(:,:) = 0._r8 + site_in%rx_fmort_rate_ustory(:,:) = 0._r8 + site_in%rx_fmort_carbonflux_ustory(:) = 0._r8 + site_in%rx_fmort_carbonflux_canopy(:) = 0._r8 + site_in%rx_fmort_rate_cambial(:,:) = 0._r8 + site_in%rx_fmort_rate_crown(:,:) = 0._r8 site_in%term_abg_flux(:,:) = 0._r8 site_in%imort_abg_flux(:,:) = 0._r8 site_in%fmort_abg_flux(:,:) = 0._r8 + site_in%nonrx_fmort_abg_flux(:,:) = 0._r8 + site_in%rx_fmort_abg_flux(:,:) = 0._r8 ! fusoin-induced growth flux of individuals site_in%growthflux_fusion(:,:) = 0._r8 @@ -353,11 +404,18 @@ subroutine zero_site( site_in ) site_in%fmort_rate_ustory_damage(:,:,:) = 0._r8 site_in%fmort_cflux_canopy_damage(:,:) = 0._r8 site_in%fmort_cflux_ustory_damage(:,:) = 0._r8 + site_in%nonrx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%nonrx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%nonrx_fmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%nonrx_fmort_cflux_ustory_damage(:,:) = 0._r8 + site_in%rx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + site_in%rx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + site_in%rx_fmort_cflux_canopy_damage(:,:) = 0._r8 + site_in%rx_fmort_cflux_ustory_damage(:,:) = 0._r8 ! Resources management (logging/harvesting, etc) site_in%resources_management%harvest_debt = 0.0_r8 site_in%resources_management%harvest_debt_sec = 0.0_r8 - site_in%resources_management%trunk_product_site = 0.0_r8 ! canopy spread site_in%spread = 0._r8 @@ -1008,6 +1066,13 @@ subroutine init_patches( nsites, sites, bc_in) currentPatch%ros_back = 0._r8 currentPatch%scorch_ht(:) = 0._r8 currentPatch%frac_burnt = 0._r8 + currentPatch%nonrx_fire = 0 + currentPatch%nonrx_frac_burnt = 0._r8 + currentPatch%nonrx_fi = 0._r8 + currentPatch%rx_fire = 0 + currentPatch%rx_fi = 0._r8 + currentPatch%rx_frac_burnt = 0._r8 + currentPatch => currentPatch%older enddo enddo @@ -1143,15 +1208,23 @@ subroutine init_cohorts(site_in, patch_in, bc_in) efstem_coh = 1.0_r8 leaf_status = leaves_on else - ! use built-in phenology - if (prt_params%season_decid(pft) == itrue .and. & - any(site_in%cstatus == [phen_cstat_nevercold, phen_cstat_iscold])) then - ! Cold deciduous, off season, assume complete abscission - efleaf_coh = 0.0_r8 - effnrt_coh = 1.0_r8 - fnrt_drop_fraction - efstem_coh = 1.0_r8 - stem_drop_fraction - leaf_status = leaves_off - else if (any(prt_params%stress_decid(pft) == [ihard_stress_decid, isemi_stress_decid])) then + ! use built-in phenology + phen_select: select case (prt_params%phen_leaf_habit(pft)) + case (ihard_season_decid) + if ( any(site_in%cstatus == [phen_cstat_nevercold, phen_cstat_iscold]) ) then + ! Cold deciduous, off season, assume complete abscission + efleaf_coh = 0.0_r8 + effnrt_coh = 1.0_r8 - fnrt_drop_fraction + efstem_coh = 1.0_r8 - stem_drop_fraction + leaf_status = leaves_off + else + ! Cold deciduous, growing season, assume leaves fully flushed + efleaf_coh = 1.0_r8 + effnrt_coh = 1.0_r8 + efstem_coh = 1.0_r8 + leaf_status = leaves_on + end if + case (ihard_stress_decid, isemi_stress_decid) ! If the plant is drought deciduous, make sure leaf status is ! always consistent with the leaf elongation factor. For tissues ! other than leaves, the actual drop fraction is a combination of the @@ -1167,14 +1240,13 @@ subroutine init_cohorts(site_in, patch_in, bc_in) else leaf_status = leaves_off end if - else - ! Evergreens, or deciduous during growing season - ! Assume leaves fully flushed - efleaf_coh = 1.0_r8 - effnrt_coh = 1.0_r8 - efstem_coh = 1.0_r8 - leaf_status = leaves_on - end if + case (ievergreen) + ! Evergreens, assume leaves fully flushed + efleaf_coh = 1.0_r8 + effnrt_coh = 1.0_r8 + efstem_coh = 1.0_r8 + leaf_status = leaves_on + end select phen_select end if if_spmode ! If positive EDPftvarcon_inst%initd is interpreted as initial recruit density. diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index bfba6d7d56..7a5bd21840 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -27,6 +27,7 @@ module EDMainMod use FatesInterfaceTypesMod , only : hlm_masterproc use FatesInterfaceTypesMod , only : numpft use FatesInterfaceTypesMod , only : hlm_use_nocomp + use FatesInterfaceTypesMod , only : ZeroBCOutCarbonFluxes use PRTGenericMod , only : prt_carbon_allom_hyp use PRTGenericMod , only : prt_cnp_flex_allom_hyp use PRTGenericMod , only : nitrogen_element @@ -46,6 +47,7 @@ module EDMainMod use EDPhysiologyMod , only : SeedUpdate use EDPhysiologyMod , only : ZeroAllocationRates use EDPhysiologyMod , only : ZeroLitterFluxes + use EDPhysiologyMod , only : PreDisturbanceLitterFluxes use EDPhysiologyMod , only : PreDisturbanceIntegrateLitter use EDPhysiologyMod , only : UpdateRecruitL2FR @@ -77,7 +79,10 @@ module EDMainMod use FatesConstantsMod , only : n_landuse_cats use FatesConstantsMod , only : nearzero use FatesConstantsMod , only : m2_per_ha + use FatesConstantsMod , only : ha_per_m2 + use FatesConstantsMod , only : days_per_sec use FatesConstantsMod , only : sec_per_day + use FatesConstantsMod , only : g_per_kg use FatesConstantsMod , only : nocomp_bareground use FatesPlantHydraulicsMod , only : do_growthrecruiteffects use FatesPlantHydraulicsMod , only : UpdateSizeDepPlantHydProps @@ -189,6 +194,9 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! Zero fluxes in and out of litter pools call ZeroLitterFluxes(currentSite) + ! Zero diagnostic bc_out carbon fluxes + call ZeroBCOutCarbonFluxes(bc_out) + ! Zero mass balance call TotalBalanceCheck(currentSite, 0) @@ -418,9 +426,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) current_fates_landuse_state_vector = currentSite%get_current_landuse_statevector() - ! Clear site GPP and AR passing to HLM - bc_out%gpp_site = 0._r8 - bc_out%ar_site = 0._r8 + ! Patch level biomass are required for C-based harvest call get_harvestable_carbon(currentSite, bc_in%site_area, bc_in%hlm_harvest_catnames, harvestable_forest_c) @@ -637,20 +643,10 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentCohort%npp_acc_hold = currentCohort%npp_acc_hold - & currentCohort%resp_excess_hold*real( hlm_days_per_year,r8) - - ! Passing gpp_acc_hold to HLM - bc_out%gpp_site = bc_out%gpp_site + currentCohort%gpp_acc_hold * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day - bc_out%ar_site = bc_out%ar_site + (currentCohort%resp_m_acc_hold + & - currentCohort%resp_g_acc_hold + currentCohort%resp_excess_hold*real(hlm_days_per_year,r8) ) * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day ! Update the mass balance tracking for the daily nutrient uptake flux ! Then zero out the daily uptakes, they have been used - ! ----------------------------------------------------------------------------- - - call EffluxIntoLitterPools(currentSite, currentPatch, currentCohort, bc_in ) @@ -685,7 +681,9 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentCohort%resp_m_acc*currentCohort%n + & currentCohort%resp_excess_hold*currentCohort%n + & currentCohort%resp_g_acc_hold*currentCohort%n/real( hlm_days_per_year,r8) - + + + call currentCohort%prt%CheckMassConservation(ft,5) ! Update the leaf biophysical rates based on proportion of leaf @@ -714,7 +712,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) ! (size --> heights of elements --> hydraulic path lengths --> ! maximum node-to-node conductances) if( (hlm_use_planthydro.eq.itrue) .and. do_growthrecruiteffects) then - call UpdateSizeDepPlantHydProps(currentSite,currentCohort, bc_in) + call UpdateSizeDepPlantHydProps(currentSite,currentCohort) call UpdateSizeDepPlantHydStates(currentSite,currentCohort) end if @@ -782,7 +780,7 @@ subroutine ed_integrate_state_variables(currentSite, bc_in, bc_out ) currentPatch => currentSite%youngest_patch do while(associated(currentPatch)) - call GenerateDamageAndLitterFluxes( currentSite, currentPatch, bc_in) + call GenerateDamageAndLitterFluxes( currentSite, currentPatch) call PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in) @@ -839,15 +837,27 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch + type(site_massbal_type), pointer :: site_cmass + real(r8) :: total_stock ! dummy variable for receiving from sitemassstock !----------------------------------------------------------------------- + site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) + ! check patch order (set second argument to true) if (debug) then call set_patchno(currentSite,.true.,1) end if + + ! Pass site-level mass fluxes to output boundary conditions + ! [kg/site/day] * [site/m2 day/sec] = [kgC/m2/s] + bc_out%gpp_site = site_cmass%gpp_acc * area_inv / sec_per_day + bc_out%ar_site = site_cmass%aresp_acc * area_inv / sec_per_day if(hlm_use_sp.eq.ifalse .and. (.not.is_restarting))then - call canopy_spread(currentSite) + call canopy_spread(currentSite) + else + site_cmass%gpp_acc = 0._r8 + site_cmass%aresp_acc = 0._r8 end if call TotalBalanceCheck(currentSite,6) @@ -905,6 +915,23 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) endif endif + ! report summary diagnostic values of FATES carbon mass pools for HLM to include in total land stocks + call SiteMassStock(currentSite,carbon12_element,total_stock,& + bc_out%veg_c_si, bc_out%litter_cwd_c_si, bc_out%seed_c_si) + + ! because the outputs of SiteMassStock are in kg C/ha, convert units to g C/m2 + bc_out%veg_c_si = bc_out%veg_c_si * g_per_kg * AREA_INV + bc_out%litter_cwd_c_si = bc_out%litter_cwd_c_si * g_per_kg * AREA_INV + bc_out%seed_c_si = bc_out%seed_c_si * g_per_kg * AREA_INV + + ! Set boundary condition to HLM for carbon loss to atm from fires and grazing + ! [kgC/ha/day]*[ha/m2]*[day/s] = [kg/m2/s] + + bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * ha_per_m2 * days_per_sec + bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * ha_per_m2 * days_per_sec + + + end subroutine ed_update_site !-------------------------------------------------------------------------------! @@ -1153,14 +1180,6 @@ subroutine bypass_dynamics(currentSite, bc_out) ! Shouldn't need to zero any nutrient fluxes ! as they should just be zero, no uptake ! in ST3 mode. - - ! Passing - bc_out%gpp_site = bc_out%gpp_site + currentCohort%gpp_acc_hold * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day - bc_out%ar_site = bc_out%ar_site + (currentCohort%resp_m_acc_hold + & - currentCohort%resp_g_acc_hold + & - currentCohort%resp_excess_hold*real( hlm_days_per_year,r8)) * & - AREA_INV * currentCohort%n / real( hlm_days_per_year,r8) / sec_per_day currentCohort => currentCohort%taller enddo diff --git a/main/EDParamsMod.F90 b/main/EDParamsMod.F90 index 92d8178ab0..1518f75be5 100644 --- a/main/EDParamsMod.F90 +++ b/main/EDParamsMod.F90 @@ -1,8 +1,8 @@ module EDParamsMod - ! - ! module that deals with reading the ED parameter file - ! + ! + ! module that deals with reading the ED parameter file + ! use FatesConstantsMod, only : r8 => fates_r8 use FatesConstantsMod, only : nearzero @@ -12,39 +12,39 @@ module EDParamsMod use FatesConstantsMod, only : fates_unset_r8 use FatesConstantsMod, only : n_landuse_cats - ! CIME Globals + ! CIME Globals use shr_log_mod , only : errMsg => shr_log_errMsg implicit none private save - ! - ! this is what the user can use for the actual values - ! - - real(r8),protected, public :: vai_top_bin_width ! width in VAI units of uppermost leaf+stem - ! layer scattering element in each canopy layer [m2/m2] - real(r8),protected, public :: vai_width_increase_factor ! factor by which each leaf+stem scattering element - ! increases in VAI width (1 = uniform spacing) - real(r8),protected, public :: photo_temp_acclim_timescale ! Length of the window for the exponential moving average (ema) - ! of vegetation temperature used in photosynthesis and respiration - ! temperature acclimation [days] - real(r8),protected, public :: photo_temp_acclim_thome_time ! Length of the window for the long-term exponential moving average (ema) - ! of vegetation temperature used in photosynthesis - ! T_home term in Kumarathunge parameterization [years] - real(r8),protected, public :: sdlng_emerg_h2o_timescale !Length of the window for the exponential moving - !average of smp used to calculate seedling emergence - real(r8),protected, public :: sdlng_mort_par_timescale !Length of the window for the exponential moving average - !of par at the seedling layer used to calculate - !seedling mortality - real(r8),protected, public :: sdlng_mdd_timescale !Length of the window for the exponential moving average - ! of moisture deficit days used to calculate seedling mortality - real(r8),protected, public :: sdlng2sap_par_timescale !Length of the window for the exponential - !moving average of par at the seedling layer used to - !calculate seedling to sapling transition rates + ! + ! this is what the user can use for the actual values + ! + + real(r8),protected, public :: vai_top_bin_width ! width in VAI units of uppermost leaf+stem + ! layer scattering element in each canopy layer [m2/m2] + real(r8),protected, public :: vai_width_increase_factor ! factor by which each leaf+stem scattering element + ! increases in VAI width (1 = uniform spacing) + real(r8),protected, public :: photo_temp_acclim_timescale ! Length of the window for the exponential moving average (ema) + ! of vegetation temperature used in photosynthesis and respiration + ! temperature acclimation [days] + real(r8),protected, public :: photo_temp_acclim_thome_time ! Length of the window for the long-term exponential moving average (ema) + ! of vegetation temperature used in photosynthesis + ! T_home term in Kumarathunge parameterization [years] + real(r8),protected, public :: sdlng_emerg_h2o_timescale !Length of the window for the exponential moving + !average of smp used to calculate seedling emergence + real(r8),protected, public :: sdlng_mort_par_timescale !Length of the window for the exponential moving average + !of par at the seedling layer used to calculate + !seedling mortality + real(r8),protected, public :: sdlng_mdd_timescale !Length of the window for the exponential moving average + ! of moisture deficit days used to calculate seedling mortality + real(r8),protected, public :: sdlng2sap_par_timescale !Length of the window for the exponential + !moving average of par at the seedling layer used to + !calculate seedling to sapling transition rates real(r8),protected, public :: fates_mortality_disturbance_fraction ! the fraction of canopy mortality that results in disturbance - real(r8),protected, public :: ED_val_comp_excln ! weighting factor for canopy layer exclusion and promotion + real(r8),protected, public :: comp_excln_exp ! weighting factor (exponent) for canopy layer exclusion and promotion real(r8),protected, public :: ED_val_vai_top_bin_width ! width in VAI units of uppermost leaf+stem layer scattering element real(r8),protected, public :: ED_val_vai_width_increase_factor ! factor by which each leaf+stem scattering element increases in VAI width real(r8),protected, public :: ED_val_nignitions ! number of annual ignitions per square km @@ -64,33 +64,38 @@ module EDParamsMod real(r8),protected, public :: ED_val_patch_fusion_tol ! minimum fraction in difference in profiles between patches real(r8),protected, public :: ED_val_canopy_closure_thresh ! site-level canopy closure point where trees take on forest (narrow) versus savannah (wide) crown allometry - logical,protected, public :: active_crown_fire ! flag, 1=active crown fire 0=no active crown fire + logical,protected, public :: active_crown_fire ! flag, 1=active crown fire 0=no active crown fire + character(len=param_string_length),parameter :: fates_name_active_crown_fire = "fates_fire_active_crown_fire" - real(r8), protected, public :: cg_strikes ! fraction of cloud to ground lightning strikes (0-1) + real(r8), protected, public :: cg_strikes ! fraction of cloud to ground lightning strikes (0-1) character(len=param_string_length),parameter :: fates_name_cg_strikes="fates_fire_cg_strikes" - ! Global identifier of how nutrients interact with the host land model - ! either they are fully coupled, or they generate uptake rates synthetically - ! in prescribed mode. In the latter, there is both NO mass removed from the HLM's soil - ! BGC N and P pools, and there is also none removed. + ! Global identifier of how nutrients interact with the host land model + ! either they are fully coupled, or they generate uptake rates synthetically + ! in prescribed mode. In the latter, there is both NO mass removed from the HLM's soil + ! BGC N and P pools, and there is also none removed. integer, public :: n_uptake_mode integer, public :: p_uptake_mode - real(r8), parameter, public :: soil_tfrz_thresh = -2.0_r8 ! Soil temperature threshold below which hydraulic failure mortality is off (non-hydro only) in degrees C + real(r8), parameter, public :: soil_tfrz_thresh = -2.0_r8 ! Soil temperature threshold below which hydraulic failure mortality is off (non-hydro only) in degrees C - integer, parameter, public :: nclmax = 2 ! Maximum number of canopy layers (used only for scratch arrays) + integer, parameter, public :: nclmax = 3 ! Maximum number of canopy layers allowed ! We would make this even higher, but making this ! a little lower keeps the size down on some output arrays ! For large arrays at patch level we use dynamic allocation - ! parameters that govern the VAI (LAI+SAI) bins used in radiative transfer code - integer, parameter, public :: nlevleaf = 30 ! number of leaf+stem layers in each canopy layer + ! parameters that govern the VAI (LAI+SAI) bins used in radiative transfer code + integer, parameter, public :: nlevleaf = 30 ! number of leaf+stem layers in each canopy layer - real(r8), public :: dinc_vai(nlevleaf) = fates_unset_r8 ! VAI bin widths array - real(r8), public :: dlower_vai(nlevleaf) = fates_unset_r8 ! lower edges of VAI bins - + real(r8), public :: dinc_vai(nlevleaf) = fates_unset_r8 ! VAI bin widths array + real(r8), public :: dlower_vai(nlevleaf) = fates_unset_r8 ! numericaly (not vertically) lower edges of VAI bins + ! starting with zero in the first index, the last bin + ! is assumed to be bounded, but a user can override this + ! if change a local parameter vai_capping in tree_lai() + ! in the allometry module + integer, parameter, public :: maxpft = 16 ! maximum number of PFTs allowed real(r8),protected,public :: q10_mr ! Q10 for respiration rate (for soil fragmenation and plant respiration) (unitless) @@ -264,10 +269,23 @@ module EDParamsMod public :: FatesRegisterParams public :: FatesReceiveParams public :: FatesReportParams - -contains + public :: GetNVegLayers + + + contains + + function GetNVegLayers(treevai) result(nv) + + real(r8) :: treevai ! The LAI+SAI of the cohort (m2/m2) + integer :: nv + + nv = count(treevai .gt. dlower_vai(:)) + + end function GetNVegLayers + !----------------------------------------------------------------------- + subroutine FatesParamsInit() ! Initialize all parameters to nan to ensure that we get valid ! values back from the host. @@ -285,7 +303,7 @@ subroutine FatesParamsInit() sdlng2sap_par_timescale = nan photo_temp_acclim_thome_time = nan fates_mortality_disturbance_fraction = nan - ED_val_comp_excln = nan + comp_excln_exp = nan ED_val_vai_top_bin_width = nan ED_val_vai_width_increase_factor = nan ED_val_nignitions = nan @@ -592,7 +610,7 @@ subroutine FatesReceiveParams(fates_params) data=fates_mortality_disturbance_fraction) call fates_params%RetrieveParameter(name=ED_name_comp_excln, & - data=ED_val_comp_excln) + data=comp_excln_exp) call fates_params%RetrieveParameter(name=ED_name_vai_top_bin_width, & data=ED_val_vai_top_bin_width) @@ -807,7 +825,7 @@ subroutine FatesReportParams(is_master) write(fates_log(),fmt0) 'photo_temp_acclim_thome_time (years) = ',photo_temp_acclim_thome_time write(fates_log(),fmti) 'hydr_htftype_node = ',hydr_htftype_node write(fates_log(),fmt0) 'fates_mortality_disturbance_fraction = ',fates_mortality_disturbance_fraction - write(fates_log(),fmt0) 'ED_val_comp_excln = ',ED_val_comp_excln + write(fates_log(),fmt0) 'comp_excln_exp = ',comp_excln_exp write(fates_log(),fmt0) 'ED_val_vai_top_bin_width = ',ED_val_vai_top_bin_width write(fates_log(),fmt0) 'ED_val_vai_width_increase_factor = ',ED_val_vai_width_increase_factor write(fates_log(),fmt0) 'ED_val_nignitions = ',ED_val_nignitions diff --git a/main/EDPftvarcon.F90 b/main/EDPftvarcon.F90 index 319fc5ff51..835ffab36f 100644 --- a/main/EDPftvarcon.F90 +++ b/main/EDPftvarcon.F90 @@ -26,6 +26,7 @@ module EDPftvarcon use FatesInterfaceTypesMod, only : hlm_nitrogen_spec, hlm_phosphorus_spec use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : hlm_nu_com + use FatesConstantsMod , only : ievergreen use FatesConstantsMod , only : prescribed_p_uptake use FatesConstantsMod , only : prescribed_n_uptake use FatesConstantsMod , only : coupled_p_uptake @@ -1673,9 +1674,8 @@ subroutine FatesCheckParams(is_master) ! This subroutine performs logical checks on user supplied parameters. It cross ! compares various parameters and will fail if they don't make sense. ! Examples: - ! A tree can not be defined as both evergreen and deciduous. A woody plant - ! cannot have a structural biomass allometry intercept of 0, and a non-woody - ! plant (grass) can't have a non-zero intercept... + ! A woody plant cannot have a structural biomass allometry intercept of 0, and a + ! non-woody plant (grass) can't have a non-zero intercept... ! ----------------------------------------------------------------------------------- use FatesConstantsMod , only : fates_check_param_set use FatesConstantsMod , only : itrue, ifalse @@ -1962,7 +1962,7 @@ subroutine FatesCheckParams(is_master) ! Check if the fraction of storage used for flushing deciduous trees ! is greater than zero, and less than or equal to 1. - if (prt_params%evergreen(ipft) == ifalse) then + if (prt_params%phen_leaf_habit(ipft) /= ievergreen) then if ( ( EDPftvarcon_inst%phenflush_fraction(ipft) < nearzero ) .or. & ( EDPFtvarcon_inst%phenflush_fraction(ipft) > 1 ) ) then @@ -1970,7 +1970,8 @@ subroutine FatesCheckParams(is_master) write(fates_log(),*) ' on bud-burst. If phenflush_fraction is not greater than 0' write(fates_log(),*) ' it will not be able to put out any leaves. Plants need leaves.' write(fates_log(),*) ' PFT#: ',ipft - write(fates_log(),*) ' evergreen flag: (should be 0):',int(prt_params%evergreen(ipft)) + write(fates_log(),*) ' phen_leaf_habit: (evergreen should be ',ievergreen,'):', & + int(prt_params%phen_leaf_habit(ipft)) write(fates_log(),*) ' phenflush_fraction: ', EDPFtvarcon_inst%phenflush_fraction(ipft) write(fates_log(),*) ' Aborting' call endrun(msg=errMsg(sourcefile, __LINE__)) diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index c21cdd6fe1..90be4df5ec 100644 --- a/main/EDTypesMod.F90 +++ b/main/EDTypesMod.F90 @@ -129,7 +129,6 @@ module EDTypesMod ! number densities of cohorts to prevent FPEs ! special mode to cause PFTs to create seed mass of all currently-existing PFTs - logical, parameter, public :: homogenize_seed_pfts = .false. character(len=*), parameter, private :: sourcefile = __FILE__ !************************************ @@ -138,7 +137,6 @@ module EDTypesMod !************************************ type, public :: ed_resources_management_type - real(r8) :: trunk_product_site ! Actual trunk product at site level KgC/site real(r8) :: harvest_debt ! the amount of kgC per site that did not successfully harvested real(r8) :: harvest_debt_sec ! the amount of kgC per site from secondary patches that did ! not successfully harvested @@ -456,6 +454,12 @@ module EDTypesMod real(r8) :: NF ! daily ignitions in km2 real(r8) :: NF_successful ! daily ignitions in km2 that actually lead to fire class(fire_weather), pointer :: fireWeather ! fire weather object + integer :: rx_flag ! daily burn window flag + real(r8) :: rxfire_area_fuel ! daily total burnable area [m2] when burn window present and fuel condition met + real(r8) :: rxfire_area_fi ! daily total burnable area [m2] when burn window present, fuel and fire intensity condition met + real(r8) :: rxfire_area_final ! daily total burnable area [m2] when all conditions met + + ! PLANT HYDRAULICS type(ed_site_hydr_type), pointer :: si_hydr @@ -496,6 +500,12 @@ module EDTypesMod real(r8) :: fmort_crownarea_canopy ! crownarea of canopy indivs killed due to fire per year. [m2/sec] real(r8) :: fmort_crownarea_ustory ! crownarea of understory indivs killed due to fire per year [m2/sec] + real(r8) :: rx_fmort_crownarea_canopy ! crownarea of canopy indivs killed due to precribed fire per year [m2/sec] + real(r8) :: rx_fmort_crownarea_ustory ! crownarea of undertsory indivs killed due to prescribed fire per year [m2/sec] + real(r8) :: nonrx_fmort_crownarea_canopy ! crownarea of canopy indivs killed due to wildfire per year [m2/sec] + real(r8) :: nonrx_fmort_crownarea_ustory ! crownarea of understory indivs killed due to wildfire per year [m2/sec] + + real(r8), allocatable :: term_nindivs_canopy(:,:,:) ! number of canopy individuals that were in cohorts which ! were terminated this timestep, by termination type, size x pft @@ -509,10 +519,16 @@ module EDTypesMod real(r8), allocatable :: imort_carbonflux(:) ! biomass of individuals killed due to impact mortality per year, by pft. [kgC/m2/sec] real(r8), allocatable :: fmort_carbonflux_canopy(:) ! biomass of canopy indivs killed due to fire per year, by pft. [gC/m2/sec] real(r8), allocatable :: fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to fire per year, by pft [gC/m2/sec] + real(r8), allocatable :: rx_fmort_carbonflux_canopy(:) ! biomass of cnaopy indivs killed due to prescribed fire per year [gC/m2/sec] + real(r8), allocatable :: rx_fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to prescribed fire per year [gC/m2/sec] + real(r8), allocatable :: nonrx_fmort_carbonflux_canopy(:) ! biomass of canopy indivs killed due to wildfire per year [gC/m2/sec] + real(r8), allocatable :: nonrx_fmort_carbonflux_ustory(:) ! biomass of understory indivs killed due to wildfire per year [gC/m2/sec] - real(r8), allocatable :: term_abg_flux(:,:) ! aboveground biomass lost due to termination mortality x size x pft - real(r8), allocatable :: imort_abg_flux(:,:) ! aboveground biomass lost due to impact mortality x size x pft [kgC/m2/sec] - real(r8), allocatable :: fmort_abg_flux(:,:) ! aboveground biomass lost due to fire mortality x size x pft + real(r8), allocatable :: term_abg_flux(:,:) ! aboveground biomass lost due to termination mortality x size x pft + real(r8), allocatable :: imort_abg_flux(:,:) ! aboveground biomass lost due to impact mortality x size x pft [kgC/m2/sec] + real(r8), allocatable :: fmort_abg_flux(:,:) ! aboveground biomass lost due to total fire mortality x size x pft + real(r8), allocatable :: rx_fmort_abg_flux(:,:) ! aboveground biomass loss due to precribed fire mortality x size x pft + real(r8), allocatable :: nonrx_fmort_abg_flux(:,:) ! aboveground biomass loss due to wildfire mortality x size x pft real(r8) :: demotion_carbonflux ! biomass of demoted individuals from canopy to understory [kgC/ha/day] @@ -533,6 +549,16 @@ module EDTypesMod real(r8), allocatable :: fmort_rate_crown(:,:) ! rate of individuals killed due to fire mortality ! from crown damage per year. on size x pft array + real(r8), allocatable :: rx_fmort_rate_canopy(:,:) ! rate of canopy individuals killed due to prescribed fire per year + real(r8), allocatable :: rx_fmort_rate_ustory(:,:) ! rate of understory individuals killed due to precribed fire per yr + real(r8), allocatable :: rx_fmort_rate_cambial(:,:) ! cambial mortality rate due to prescribed fire + real(r8), allocatable :: rx_fmort_rate_crown(:,:) ! crown damage mortality due to prescribed fire + + real(r8), allocatable :: nonrx_fmort_rate_canopy(:,:) ! rate of canopy indivs killed due to wildfire per year + real(r8), allocatable :: nonrx_fmort_rate_ustory(:,:) ! rate of understory indivs killed due to wildfire per year + real(r8), allocatable :: nonrx_fmort_rate_cambial(:,:) ! cambial mortality rate due to wildfire + real(r8), allocatable :: nonrx_fmort_rate_crown(:,:) ! crown damage mortality due to wildfire + real(r8), allocatable :: imort_rate_damage(:,:,:) ! number of individuals per damage class that die from impact mortality real(r8), allocatable :: term_nindivs_canopy_damage(:,:,:) ! number of individuals per damage class that die from termination mortality - canopy real(r8), allocatable :: term_nindivs_ustory_damage(:,:,:) ! number of individuals per damage class that die from termination mortality - canopy @@ -540,6 +566,14 @@ module EDTypesMod real(r8), allocatable :: fmort_rate_ustory_damage(:,:,:) ! number of individuals per damage class that die from fire - ustory real(r8), allocatable :: fmort_cflux_canopy_damage(:,:) ! cflux per damage class that die from fire - canopy real(r8), allocatable :: fmort_cflux_ustory_damage(:,:) ! cflux per damage class that die from fire - ustory + real(r8), allocatable :: rx_fmort_rate_canopy_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -canopy + real(r8), allocatable :: rx_fmort_rate_ustory_damage(:,:,:) ! number of indivs per damage class that die from precribed fire -understory + real(r8), allocatable :: rx_fmort_cflux_canopy_damage(:,:) ! cflux per damage class that die from prescribed fire -canopy + real(r8), allocatable :: rx_fmort_cflux_ustory_damage(:,:) ! cflux per damage class that die from precribed fire - understory + real(r8), allocatable :: nonrx_fmort_rate_canopy_damage(:,:,:) !number of indivs per damage class that die from wildfire -canopy + real(r8), allocatable :: nonrx_fmort_rate_ustory_damage(:,:,:) !number of indivs per damage class that die from wildfire -understory + real(r8), allocatable :: nonrx_fmort_cflux_canopy_damage(:,:) !cflux per damage class that die from wildfire - canopy + real(r8), allocatable :: nonrx_fmort_cflux_ustory_damage(:,:) !cflux per damage class that die from wildfire - understory real(r8), allocatable :: imort_cflux_damage(:,:) ! carbon flux from impact mortality by damage class [kgC/m2/sec] real(r8), allocatable :: term_cflux_canopy_damage(:,:) ! carbon flux from termination mortality by damage class real(r8), allocatable :: term_cflux_ustory_damage(:,:) ! carbon flux from termination mortality by damage class diff --git a/main/FatesConstantsMod.F90 b/main/FatesConstantsMod.F90 index 0c69199f4c..4d535f9de4 100644 --- a/main/FatesConstantsMod.F90 +++ b/main/FatesConstantsMod.F90 @@ -72,21 +72,37 @@ module FatesConstantsMod ! but is shedding them (partial shedding). This plant ! should not allocate carbon towards growth or ! reproduction. -integer, parameter, public :: ihard_stress_decid = 1 ! If the PFT is stress (drought) deciduous, - ! this flag is used to tell that the PFT - ! is a "hard" deciduous (i.e., the plant - ! has only two statuses, the plant either - ! sheds all leaves when it's time, or seeks - ! to flush the leaves back to allometry - ! when conditions improve. -integer, parameter, public :: isemi_stress_decid = 2 ! If the PFT is stress (drought) deciduous, - ! this flag is used to tell that the PFT - ! is a semi-deciduous (i.e., the plant - ! can downregulate the amount of leaves - ! relative to the allometry based on - ! soil moisture conditions. It can still - ! shed all leaves if conditions are very - ! dry. +integer, parameter, public :: ievergreen = 1 ! Flag that indicates that the leaf phenology + ! habit for a PFT is evergreen. This means + ! that seasonal environmental conditions do + ! not directly impact leaf biomass, although + ! the total leaf biomass can fall below + ! allometry if the plant's NPP is negative + ! and causes a significant depletion of the + ! storage pool. +integer, parameter, public :: ihard_season_decid = 2 ! Flag that indicates that the leaf phenology + ! habit for a PFT is a "hard" season (cold) + ! deciduous. This means that the plant + ! has only two statuses, the plant either + ! abscises all leaves when conditions + ! deteriorate, or flushes leaves to bring it + ! back to allometry when conditions improve. +integer, parameter, public :: ihard_stress_decid = 3 ! Flag that indicates that the leaf phenology + ! habit for a PFT is a "hard" stress + ! (drought) deciduous. This means that the + ! plant has only two statuses, the plant + ! either abscises all leaves when conditions + ! deteriorate, or flushes leaves to bring it + ! back to allometry when conditions improve +integer, parameter, public :: isemi_stress_decid = 4 ! Flag that indicates that the leaf phenology + ! habit for a PFT is a stress (hydro) + ! semi-deciduous. This means that the plant + ! can partially abscise or flush leaves + ! based on water availability, and + ! conditions. It can still abscise all leaves + ! when conditions are very dry, and flush all + ! leaves back to allometry when water is not + ! limiting. integer, parameter, public :: ican_upper = 1 ! nominal index for the upper canopy integer, parameter, public :: ican_ustory = 2 ! nominal index for diagnostics that refer to understory layers diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 8658adfc52..ddf3b75bf2 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -55,7 +55,7 @@ module FatesHistoryInterfaceMod use FatesInterfaceTypesMod , only : hlm_freq_day use FatesInterfaceTypesMod , only : hlm_parteh_mode use FatesInterfaceTypesMod , only : hlm_use_sp - use EDParamsMod , only : ED_val_comp_excln + use EDParamsMod , only : comp_excln_exp use EDParamsMod , only : ED_val_phen_coldtemp use EDParamsMod , only : nlevleaf use EDParamsMod , only : ED_val_history_height_bin_edges @@ -291,8 +291,8 @@ module FatesHistoryInterfaceMod integer :: ih_pdemand_scpf integer :: ih_trimming_si - integer :: ih_area_plant_si - integer :: ih_area_trees_si + integer :: ih_fracarea_plant_si + integer :: ih_fracarea_trees_si integer :: ih_litter_in_elem integer :: ih_litter_out_elem integer :: ih_seed_bank_elem @@ -357,7 +357,7 @@ module FatesHistoryInterfaceMod integer :: ih_primaryland_fusion_error_si ! land-use-resolved variables - integer :: ih_area_si_landuse + integer :: ih_fracarea_si_landuse integer :: ih_biomass_si_landuse integer :: ih_burnedarea_si_landuse integer :: ih_gpp_si_landuse @@ -390,6 +390,7 @@ module FatesHistoryInterfaceMod ! Indices to site by patch age by pft variables integer :: ih_biomass_si_agepft integer :: ih_npp_si_agepft + integer :: ih_scorch_height_si_pft integer :: ih_scorch_height_si_agepft ! Indices to (site) variables @@ -429,7 +430,6 @@ module FatesHistoryInterfaceMod integer :: ih_froot_mr_si integer :: ih_livestem_mr_si integer :: ih_livecroot_mr_si - integer :: ih_woodproduct_si integer :: ih_h2oveg_si integer :: ih_h2oveg_dead_si integer :: ih_h2oveg_recruit_si @@ -448,17 +448,27 @@ module FatesHistoryInterfaceMod integer :: ih_nesterov_fire_danger_si integer :: ih_fire_nignitions_si integer :: ih_fire_fdi_si - integer :: ih_fire_intensity_area_product_si + integer :: ih_fire_intensity_fracarea_product_si + integer :: ih_nonrx_intensity_fracarea_product_si + integer :: ih_rx_intensity_fracarea_product_si integer :: ih_spitfire_ros_si integer :: ih_effect_wspeed_si integer :: ih_tfc_ros_si integer :: ih_fire_intensity_si - integer :: ih_fire_area_si + integer :: ih_nonrx_intensity_si + integer :: ih_fire_fracarea_si + integer :: ih_nonrx_fracarea_si integer :: ih_fire_fuel_bulkd_si integer :: ih_fire_fuel_eff_moist_si integer :: ih_fire_fuel_sav_si integer :: ih_fire_fuel_mef_si integer :: ih_sum_fuel_si + integer :: ih_rx_burn_window_si + integer :: ih_rx_intensity_si + integer :: ih_rx_fracarea_si + integer :: ih_rx_fracarea_fuel_si + integer :: ih_rx_fracarea_fi_si + integer :: ih_rx_fracarea_final_si integer :: ih_fragmentation_scaler_sl integer :: ih_nplant_si_scpf @@ -503,9 +513,12 @@ module FatesHistoryInterfaceMod integer :: ih_m9_si_scpf integer :: ih_m10_si_scpf integer :: ih_m11_si_scpf + integer :: ih_m12_si_scpf - integer :: ih_crownfiremort_si_scpf - integer :: ih_cambialfiremort_si_scpf + integer :: ih_nonrx_crown_mort_si_scpf + integer :: ih_nonrx_cambial_mort_si_scpf + integer :: ih_rx_crown_mort_si_scpf + integer :: ih_rx_cambial_mort_si_scpf integer :: ih_abg_mortality_cflux_si_scpf integer :: ih_abg_productivity_cflux_si_scpf @@ -541,8 +554,8 @@ module FatesHistoryInterfaceMod integer :: ih_promotion_rate_si_scls integer :: ih_trimming_canopy_si_scls integer :: ih_trimming_understory_si_scls - integer :: ih_crown_area_canopy_si_scls - integer :: ih_crown_area_understory_si_scls + integer :: ih_crown_fracarea_canopy_si_scls + integer :: ih_crown_fracarea_understory_si_scls integer :: ih_ddbh_canopy_si_scls integer :: ih_ddbh_understory_si_scls integer :: ih_agb_si_scls @@ -560,6 +573,7 @@ module FatesHistoryInterfaceMod integer :: ih_m8_si_scls integer :: ih_m9_si_scls integer :: ih_m10_si_scls + integer :: ih_m12_si_scls integer :: ih_m10_si_cacls integer :: ih_nplant_si_cacls @@ -643,25 +657,41 @@ module FatesHistoryInterfaceMod integer :: ih_ungerm_seed_bank_si_pft ! carbon only integer :: ih_seedling_pool_si_pft ! carbon only + ! Non-per-ageclass equivalents of per-ageclass variables + integer :: ih_canopy_fracarea_si + integer :: ih_ncl_si + integer :: ih_fracarea_si + ! indices to (site x patch-age) variables - integer :: ih_area_si_age + integer :: ih_fracarea_si_age integer :: ih_lai_si_age - integer :: ih_canopy_area_si_age + integer :: ih_canopy_fracarea_si_age integer :: ih_gpp_si_age integer :: ih_npp_si_age integer :: ih_ncl_si_age integer :: ih_npatches_si_age + integer :: ih_zstar_si integer :: ih_zstar_si_age integer :: ih_biomass_si_age integer :: ih_c_stomata_si_age integer :: ih_c_lblayer_si_age + integer :: ih_agesince_anthrodist_si integer :: ih_agesince_anthrodist_si_age integer :: ih_secondarylands_area_si_age integer :: ih_primarylands_area_si_age integer :: ih_area_burnt_si_age + integer :: ih_primarylands_fracarea_si + integer :: ih_secondarylands_fracarea_si + integer :: ih_secondarylands_fracarea_si_age + integer :: ih_primarylands_fracarea_si_age + integer :: ih_fracarea_burnt_si_age + integer :: ih_rx_fracarea_burnt_si_age + integer :: ih_nonrx_fracarea_burnt_si_age ! integer :: ih_fire_rate_of_spread_front_si_age integer :: ih_fire_intensity_si_age integer :: ih_fire_sum_fuel_si_age + integer :: ih_rx_intensity_si_age + integer :: ih_nonrx_intensity_si_age ! indices to (site x height) variables integer :: ih_canopy_height_dist_si_height @@ -775,7 +805,7 @@ module FatesHistoryInterfaceMod integer :: ih_crownarea_cl ! indices to (patch age x fuel size class) variables - integer :: ih_fuel_amount_age_fuel + integer :: ih_fuel_amount_si_agfc ! The number of variable dim/kind types we have defined (static) @@ -820,11 +850,14 @@ module FatesHistoryInterfaceMod procedure :: assemble_history_output_types procedure :: update_history_dyn - procedure :: update_history_dyn1 - procedure :: update_history_dyn2 + procedure :: update_history_dyn_sitelevel + procedure :: update_history_dyn_subsite + procedure :: update_history_dyn_subsite_ageclass + procedure :: reset_history_dyn_subsite procedure :: update_history_hifrq - procedure :: update_history_hifrq1 - procedure :: update_history_hifrq2 + procedure :: update_history_hifrq_sitelevel + procedure :: update_history_hifrq_subsite + procedure :: update_history_hifrq_subsite_ageclass procedure :: update_history_hydraulics procedure :: update_history_nutrflux @@ -863,6 +896,7 @@ module FatesHistoryInterfaceMod ! private work functions procedure, private :: define_history_vars + procedure, private :: per_ageclass_norm_info procedure, private :: set_history_var procedure, private :: init_dim_kinds_maps procedure, private :: set_dim_indices @@ -2330,9 +2364,11 @@ subroutine update_history_dyn(this,nc,nsites,sites,bc_in) if (hlm_use_ed_st3.eq.itrue) return if(hlm_hist_level_dynam>0) then - call update_history_dyn1(this,nc,nsites,sites,bc_in) + call update_history_dyn_sitelevel(this,nc,nsites,sites) if(hlm_hist_level_dynam>1) then - call update_history_dyn2(this,nc,nsites,sites,bc_in) + call update_history_dyn_subsite(this,nc,nsites,sites,bc_in) + call update_history_dyn_subsite_ageclass(this,nc,nsites,sites) + call reset_history_dyn_subsite(this, nsites, sites) end if end if @@ -2343,8 +2379,13 @@ end subroutine update_history_dyn ! ========================================================================= - subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) + subroutine update_history_dyn_sitelevel(this,nc,nsites,sites) + ! --------------------------------------------------------------------------------- + ! This subroutine is intended to update all history variables with upfreq == + ! group_dyna_simple that are saved at the site level. So, eg., FATES_VEGC is + ! updated here, but not FATES_VEGC_PF. + ! --------------------------------------------------------------------------------- ! Arguments @@ -2352,7 +2393,6 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) type(fates_cohort_type), pointer :: ccohort type(fates_patch_type), pointer :: cpatch @@ -2388,31 +2428,43 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) real(r8) :: repro_m_net_alloc ! mass allocated to reproduction [kg/yr] real(r8) :: leaf_herbivory ! mass of leaves eaten by herbivores [kg/yr] real(r8) :: n_perm2 ! abundance per m2 - real(r8) :: area_frac ! Fraction of area for this patch + real(r8) :: patch_fracarea ! Fraction of area for this patch associate( hio_npatches_si => this%hvars(ih_npatches_si)%r81d, & hio_ncohorts_si => this%hvars(ih_ncohorts_si)%r81d, & + hio_ncl_si => this%hvars(ih_ncl_si)%r81d, & + hio_zstar_si => this%hvars(ih_zstar_si)%r81d, & hio_trimming_si => this%hvars(ih_trimming_si)%r81d, & - hio_area_plant_si => this%hvars(ih_area_plant_si)%r81d, & - hio_area_trees_si => this%hvars(ih_area_trees_si)%r81d, & + hio_fracarea_plant_si => this%hvars(ih_fracarea_plant_si)%r81d, & + hio_fracarea_trees_si => this%hvars(ih_fracarea_trees_si)%r81d, & hio_fates_fraction_si => this%hvars(ih_fates_fraction_si)%r81d, & hio_ba_weighted_height_si => this%hvars(ih_ba_weighted_height_si)%r81d, & hio_ca_weighted_height_si => this%hvars(ih_ca_weighted_height_si)%r81d, & hio_canopy_spread_si => this%hvars(ih_canopy_spread_si)%r81d, & hio_nesterov_fire_danger_si => this%hvars(ih_nesterov_fire_danger_si)%r81d, & + hio_rx_burn_window_si => this%hvars(ih_rx_burn_window_si)%r81d, & hio_fire_nignitions_si => this%hvars(ih_fire_nignitions_si)%r81d, & hio_fire_fdi_si => this%hvars(ih_fire_fdi_si)%r81d, & hio_spitfire_ros_si => this%hvars(ih_spitfire_ros_si)%r81d, & hio_tfc_ros_si => this%hvars(ih_tfc_ros_si)%r81d, & hio_effect_wspeed_si => this%hvars(ih_effect_wspeed_si)%r81d, & hio_fire_intensity_si => this%hvars(ih_fire_intensity_si)%r81d, & - hio_fire_intensity_area_product_si => this%hvars(ih_fire_intensity_area_product_si)%r81d, & - hio_fire_area_si => this%hvars(ih_fire_area_si)%r81d, & + hio_fire_intensity_fracarea_product_si => this%hvars(ih_fire_intensity_fracarea_product_si)%r81d, & + hio_fire_fracarea_si => this%hvars(ih_fire_fracarea_si)%r81d, & hio_fire_fuel_bulkd_si => this%hvars(ih_fire_fuel_bulkd_si)%r81d, & hio_fire_fuel_eff_moist_si => this%hvars(ih_fire_fuel_eff_moist_si)%r81d, & hio_fire_fuel_sav_si => this%hvars(ih_fire_fuel_sav_si)%r81d, & hio_fire_fuel_mef_si => this%hvars(ih_fire_fuel_mef_si)%r81d, & hio_sum_fuel_si => this%hvars(ih_sum_fuel_si)%r81d, & + hio_nonrx_intensity_si => this%hvars(ih_nonrx_intensity_si)%r81d, & + hio_nonrx_intensity_fracarea_product_si => this%hvars(ih_nonrx_intensity_fracarea_product_si)%r81d, & + hio_nonrx_fracarea_si => this%hvars(ih_nonrx_fracarea_si)%r81d, & + hio_rx_intensity_si => this%hvars(ih_rx_intensity_si)%r81d, & + hio_rx_intensity_fracarea_product_si => this%hvars(ih_rx_intensity_fracarea_product_si)%r81d, & + hio_rx_fracarea_si => this%hvars(ih_rx_fracarea_si)%r81d, & + hio_rx_fracarea_fuel_si => this%hvars(ih_rx_fracarea_fuel_si)%r81d, & + hio_rx_fracarea_fi_si => this%hvars(ih_rx_fracarea_fi_si)%r81d, & + hio_rx_fracarea_final_si => this%hvars(ih_rx_fracarea_final_si)%r81d, & hio_litter_in_si => this%hvars(ih_litter_in_si)%r81d, & hio_litter_out_si => this%hvars(ih_litter_out_si)%r81d, & hio_npp_si => this%hvars(ih_npp_si)%r81d, & @@ -2448,7 +2500,6 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_promotion_carbonflux_si => this%hvars(ih_promotion_carbonflux_si)%r81d, & hio_canopy_mortality_carbonflux_si => this%hvars(ih_canopy_mortality_carbonflux_si)%r81d, & hio_ustory_mortality_carbonflux_si => this%hvars(ih_understory_mortality_carbonflux_si)%r81d, & - hio_woodproduct_si => this%hvars(ih_woodproduct_si)%r81d, & hio_gdd_si => this%hvars(ih_gdd_si)%r81d, & hio_site_ncolddays_si => this%hvars(ih_site_ncolddays_si)%r81d, & hio_site_nchilldays_si => this%hvars(ih_site_nchilldays_si)%r81d, & @@ -2522,10 +2573,6 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_cleafoff_si(io_si) = real(sites(s)%phen_model_date - sites(s)%cleafoffdate,r8) hio_cleafon_si(io_si) = real(sites(s)%phen_model_date - sites(s)%cleafondate,r8) - ! track total wood product accumulation at the site level - hio_woodproduct_si(io_si) = sites(s)%resources_management%trunk_product_site & - * AREA_INV - ! site-level fire variables: ! Nesterov index (unitless) @@ -2533,6 +2580,9 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_effect_wspeed_si(io_si) = sites(s)%fireWeather%effective_windspeed/sec_per_min + ! Prescribed fire burn window + hio_rx_burn_window_si(io_si) = hio_rx_burn_window_si(io_si) + sites(s)%fireWeather%rx_flag + ! number of ignitions [#/km2/day -> #/m2/s] hio_fire_nignitions_si(io_si) = sites(s)%NF_successful / m2_per_km2 / & sec_per_day @@ -2540,6 +2590,15 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) ! Fire danger index (FDI) (0-1) hio_fire_fdi_si(io_si) = sites(s)%FDI + ! total rx burnable fraction when fuel condition met + hio_rx_fracarea_fuel_si(io_si) = sites(s)%rxfire_area_fuel * AREA_INV + + ! total rx burnable fraction when fuel and FI conditions met + hio_rx_fracarea_fi_si(io_si) = sites(s)%rxfire_area_fi * AREA_INV + + ! total rx burnable fraction when all conditions met + hio_rx_fracarea_final_si(io_si) = sites(s)%rxfire_area_final * AREA_INV + ! If hydraulics are turned on, track the error terms associated with ! dynamics [kg/m2] if(hlm_use_planthydro.eq.itrue)then @@ -2631,6 +2690,14 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_elai_si(io_si) = hio_elai_si(io_si) + sum( cpatch%canopy_area_profile(:,:,:) * cpatch%elai_profile(:,:,:) ) * & cpatch%total_canopy_area * AREA_INV + hio_ncl_si(io_si) = hio_ncl_si(io_si) + cpatch%ncl_p * cpatch%area * AREA_INV + + ! only valid when "strict ppa" enabled + if ( comp_excln_exp .lt. 0._r8 ) then + hio_zstar_si(io_si) = hio_zstar_si(io_si) & + + cpatch%zstar * cpatch%area * AREA_INV + end if + ! 24hr veg temperature hio_tveg24(io_si) = hio_tveg24(io_si) + & (cpatch%tveg24%GetMean()- t_water_freeze_k_1atm)*cpatch%area*AREA_INV @@ -2648,9 +2715,9 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_trimming_si(io_si) = hio_trimming_si(io_si) + cpatch%tallest%canopy_trim * cpatch%area * AREA_INV endif - ! area occupied by plants and trees [m2/m2] - hio_area_plant_si(io_si) = hio_area_plant_si(io_si) + min(cpatch%total_canopy_area,cpatch%area) * AREA_INV - hio_area_trees_si(io_si) = hio_area_trees_si(io_si) + min(cpatch%total_tree_area,cpatch%area) * AREA_INV + ! fractional area occupied by plants and trees [m2/m2] + hio_fracarea_plant_si(io_si) = hio_fracarea_plant_si(io_si) + min(cpatch%total_canopy_area,cpatch%area) * AREA_INV + hio_fracarea_trees_si(io_si) = hio_fracarea_trees_si(io_si) + min(cpatch%total_tree_area,cpatch%area) * AREA_INV ! Patch specific variables that are already calculated ! These things are all duplicated. Should they all be converted to LL or array structures RF? @@ -2660,19 +2727,29 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) hio_spitfire_ros_si(io_si) = hio_spitfire_ros_si(io_si) + cpatch%ROS_front * cpatch%area * AREA_INV / sec_per_min hio_tfc_ros_si(io_si) = hio_tfc_ros_si(io_si) + cpatch%TFC_ROS * cpatch%area * AREA_INV hio_fire_intensity_si(io_si) = hio_fire_intensity_si(io_si) + cpatch%FI * cpatch%area * AREA_INV * J_per_kJ - hio_fire_area_si(io_si) = hio_fire_area_si(io_si) + cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day + hio_fire_fracarea_si(io_si) = hio_fire_fracarea_si(io_si) + cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day + hio_nonrx_intensity_si(io_si) = hio_nonrx_intensity_si(io_si) + cpatch%nonrx_FI * cpatch%area * AREA_INV * J_per_kJ + hio_nonrx_fracarea_si(io_si) = hio_nonrx_fracarea_si(io_si) + cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV / sec_per_day + hio_rx_intensity_si(io_si) = hio_rx_intensity_si(io_si) + cpatch%rx_FI * cpatch%area * AREA_INV * J_per_kJ + hio_rx_fracarea_si(io_si) = hio_rx_fracarea_si(io_si) + cpatch%rx_frac_burnt * cpatch%area * AREA_INV / sec_per_day hio_fire_fuel_bulkd_si(io_si) = hio_fire_fuel_bulkd_si(io_si) + cpatch%fuel%bulk_density_notrunks * cpatch%area * AREA_INV hio_fire_fuel_eff_moist_si(io_si) = hio_fire_fuel_eff_moist_si(io_si) + cpatch%fuel%average_moisture_notrunks * cpatch%area * AREA_INV hio_fire_fuel_sav_si(io_si) = hio_fire_fuel_sav_si(io_si) + cpatch%fuel%SAV_notrunks * cpatch%area * AREA_INV / m_per_cm hio_fire_fuel_mef_si(io_si) = hio_fire_fuel_mef_si(io_si) + cpatch%fuel%MEF_notrunks * cpatch%area * AREA_INV hio_sum_fuel_si(io_si) = hio_sum_fuel_si(io_si) + cpatch%fuel%non_trunk_loading * cpatch%area * AREA_INV - hio_fire_intensity_area_product_si(io_si) = hio_fire_intensity_area_product_si(io_si) + & + hio_nonrx_intensity_fracarea_product_si(io_si) = hio_nonrx_intensity_fracarea_product_si(io_si) + & + cpatch%nonrx_FI * cpatch%nonrx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ + + hio_rx_intensity_fracarea_product_si(io_si) = hio_rx_intensity_fracarea_product_si(io_si) + & + cpatch%rx_FI * cpatch%rx_frac_burnt * cpatch%area * AREA_INV * J_per_kJ + + hio_fire_intensity_fracarea_product_si(io_si) = hio_fire_intensity_fracarea_product_si(io_si) + & cpatch%FI * cpatch%frac_burnt * cpatch%area * AREA_INV * J_per_kJ litt => cpatch%litter(element_pos(carbon12_element)) - area_frac = cpatch%area * AREA_INV + patch_fracarea = cpatch%area * AREA_INV ! Sum up all output fluxes (fragmentation) kgC/m2/day -> kgC/m2/s hio_litter_out_si(io_si) = hio_litter_out_si(io_si) + & @@ -2682,29 +2759,29 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) sum(litt%bg_cwd_frag(:,:)) + & sum(litt%seed_decay(:)) + & sum(litt%seed_germ_decay(:))) * & - area_frac * days_per_sec + patch_fracarea * days_per_sec ! Sum up total seed bank (germinated and ungerminated) hio_seed_bank_si(io_si) = hio_seed_bank_si(io_si) + & (sum(litt%seed(:))+sum(litt%seed_germ(:))) * & - area_frac + patch_fracarea ! Sum up total seed bank (just ungerminated) hio_ungerm_seed_bank_si(io_si) = hio_ungerm_seed_bank_si(io_si) + & - sum(litt%seed(:)) * area_frac + sum(litt%seed(:)) * patch_fracarea ! Sum up total seedling pool hio_seedling_pool_si(io_si) = hio_seedling_pool_si(io_si) + & - sum(litt%seed_germ(:)) * area_frac + sum(litt%seed_germ(:)) * patch_fracarea ! Sum up the input flux into the seed bank (local and external) hio_seeds_in_si(io_si) = hio_seeds_in_si(io_si) + & (sum(litt%seed_in_local(:)) + sum(litt%seed_in_extern(:))) * & - area_frac * days_per_sec + patch_fracarea * days_per_sec hio_seeds_in_local_si(io_si) = hio_seeds_in_local_si(io_si) + & sum(litt%seed_in_local(:)) * & - area_frac * days_per_sec + patch_fracarea * days_per_sec ! loop through cohorts on patch ccohort => cpatch%shortest @@ -2720,15 +2797,8 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) ! Mass pools [kg] elloop: do el = 1, num_elements - sapw_m = ccohort%prt%GetState(sapw_organ, element_list(el)) - struct_m = ccohort%prt%GetState(struct_organ, element_list(el)) - leaf_m = ccohort%prt%GetState(leaf_organ, element_list(el)) - fnrt_m = ccohort%prt%GetState(fnrt_organ, element_list(el)) - store_m = ccohort%prt%GetState(store_organ, element_list(el)) - repro_m = ccohort%prt%GetState(repro_organ, element_list(el)) - - alive_m = leaf_m + fnrt_m + sapw_m - total_m = alive_m + store_m + struct_m + call ccohort%prt%GetBiomass(element_list(el), & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) ! Plant multi-element states and fluxes ! Zero states, and set the fluxes @@ -2830,11 +2900,15 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) end if end do elloop - ! FLUXES --- + ! Carbon FLUXES --- ! Flux Variables (cohorts must had experienced a day before any of these values ! have any meaning, otherwise they are just inialization values - notnew: if( .not.(ccohort%isnew) ) then + call ccohort%prt%GetBiomass(carbon12_element , & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) + + notnew: if( .not.(ccohort%isnew) ) then + hio_npp_si(io_si) = hio_npp_si(io_si) + & ccohort%npp_acc_hold * n_perm2 / days_per_year / sec_per_day @@ -2894,52 +2968,41 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) end if - ! THIS NEEDS TO BE NORMALIZED (RGK) + ! THIS NEEDS TO BE NORMALIZED hio_ca_weighted_height_si(io_si) = hio_ca_weighted_height_si(io_si) + & ccohort%height * ccohort%c_area / m2_per_ha site_ca = site_ca + ccohort%c_area / m2_per_ha - ! RGK - CANOPY/USTORY BIOMASS IS NOT A FLUX, NEED NOT BE CONDITIONED BY isnew + + ! Mortality Carbon Flux by layer ! ---------------------------------------------------------------------------------- if (ccohort%canopy_layer .eq. 1) then - hio_canopy_biomass_si(io_si) = hio_canopy_biomass_si(io_si) + n_perm2 * total_m hio_canopy_mortality_carbonflux_si(io_si) = hio_canopy_mortality_carbonflux_si(io_si) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * & - total_m * ccohort%n * days_per_sec * years_per_day * ha_per_m2 + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * total_m * & - ccohort%n * ha_per_m2 + ccohort%SumMortForHistory(per_year = .false.) * total_m * ccohort%n * ha_per_m2 hio_canopy_mortality_crownarea_si(io_si) = hio_canopy_mortality_crownarea_si(io_si) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * & - ccohort%c_area + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%c_area * sec_per_day * days_per_year + ccohort%SumMortForHistory(per_year = .true.) * ccohort%c_area else - hio_ustory_biomass_si(io_si) = hio_ustory_biomass_si(io_si) + n_perm2 * total_m hio_ustory_mortality_carbonflux_si(io_si) = hio_ustory_mortality_carbonflux_si(io_si) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * & - total_m * ccohort%n * days_per_sec * years_per_day * ha_per_m2 + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * total_m * & - ccohort%n * ha_per_m2 + ccohort%SumMortForHistory(per_year = .false.) * total_m * ccohort%n * ha_per_m2 hio_ustory_mortality_crownarea_si(io_si) = hio_ustory_mortality_crownarea_si(io_si) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * & - ccohort%c_area + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%c_area * sec_per_day * days_per_year + ccohort%SumMortForHistory(per_year = .true.) * ccohort%c_area end if - + end if notnew + if (ccohort%canopy_layer .eq. 1) then + hio_canopy_biomass_si(io_si) = hio_canopy_biomass_si(io_si) + n_perm2 * total_m + else + hio_ustory_biomass_si(io_si) = hio_ustory_biomass_si(io_si) + n_perm2 * total_m + end if + ccohort => ccohort%taller enddo cohortloop ! cohort loop @@ -2996,11 +3059,18 @@ subroutine update_history_dyn1(this,nc,nsites,sites,bc_in) end associate return - end subroutine update_history_dyn1 + end subroutine update_history_dyn_sitelevel ! ========================================================================================= - subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) + subroutine update_history_dyn_subsite(this,nc,nsites,sites,bc_in) + + ! --------------------------------------------------------------------------------- + ! This subroutine is intended to update all history variables with upfreq == + ! group_dyna_complx (i.e., that have a dimension in addition to that for the site + ! level) that do NOT include age class. So, eg., FATES_VEGC_PF is updated here, + ! but not FATES_VEGC or FATES_VEGC_APPF. + ! --------------------------------------------------------------------------------- ! Arguments class(fates_history_interface_type) :: this @@ -3041,9 +3111,9 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) real(r8) :: struct_m_net_alloc ! mass allocated to structure [kg/yr] real(r8) :: repro_m_net_alloc ! mass allocated to reproduction [kg/yr] real(r8) :: n_perm2 ! abundance per m2 - integer :: ageclass_since_anthrodist ! what is the equivalent age class for - ! time-since-anthropogenic-disturbance of secondary forest - real(r8) :: area_frac ! Fraction of area for this patch + integer :: iscag_anthrodist ! what is the equivalent age class for + ! time-since-anthropogenic-disturbance of secondary forest + real(r8) :: patch_fracarea ! Fraction of area for this patch real(r8) :: frac_canopy_in_bin ! fraction of a leaf's canopy that is within a given height bin real(r8) :: binbottom,bintop ! edges of height bins integer :: height_bin_max, height_bin_min ! which height bin a given cohort's canopy is in @@ -3053,12 +3123,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) integer :: i_cacls, i_capf ! iterators for cohort age and cohort age x pft integer :: i_fuel ! iterators for fuel dims integer :: i_heightbin ! iterator for height bins - integer :: iagepft ! age x pft index integer :: ilyr ! Soil index for nlevsoil - integer :: iscag ! size-class x age index - integer :: iscagpft ! size-class x age x pft index integer :: icdpf, icdsc, icdam ! iterators for the crown damage level - integer :: i_agefuel ! age x fuel size class index real(r8) :: gpp_cached ! gpp from previous timestep, for c13 discrimination real(r8) :: crown_depth ! Depth of the crown [m] real(r8) :: gpp_cached_scpf(numpft*nlevsclass) ! variable used to cache gpp value in previous time step; for C13 discrimination @@ -3156,9 +3222,12 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_m8_si_scpf => this%hvars(ih_m8_si_scpf)%r82d, & hio_m9_si_scpf => this%hvars(ih_m9_si_scpf)%r82d, & hio_m10_si_scpf => this%hvars(ih_m10_si_scpf)%r82d, & + hio_m12_si_scpf => this%hvars(ih_m12_si_scpf)%r82d, & hio_m10_si_capf => this%hvars(ih_m10_si_capf)%r82d, & - hio_crownfiremort_si_scpf => this%hvars(ih_crownfiremort_si_scpf)%r82d, & - hio_cambialfiremort_si_scpf => this%hvars(ih_cambialfiremort_si_scpf)%r82d, & + hio_nonrx_crown_mort_si_scpf => this%hvars(ih_nonrx_crown_mort_si_scpf)%r82d, & + hio_nonrx_cambial_mort_si_scpf => this%hvars(ih_nonrx_cambial_mort_si_scpf)%r82d, & + hio_rx_crown_mort_si_scpf => this%hvars(ih_rx_crown_mort_si_scpf)%r82d, & + hio_rx_cambial_mort_si_scpf => this%hvars(ih_rx_cambial_mort_si_scpf)%r82d, & hio_abg_mortality_cflux_si_scpf => this%hvars(ih_abg_mortality_cflux_si_scpf)%r82d, & hio_abg_productivity_cflux_si_scpf => this%hvars(ih_abg_productivity_cflux_si_scpf)%r82d, & hio_burn_flux_elem => this%hvars(ih_burn_flux_elem)%r82d, & @@ -3172,7 +3241,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_m8_si_scls => this%hvars(ih_m8_si_scls)%r82d, & hio_m9_si_scls => this%hvars(ih_m9_si_scls)%r82d, & hio_m10_si_scls => this%hvars(ih_m10_si_scls)%r82d, & - hio_m10_si_cacls => this%hvars(ih_m10_si_cacls)%r82d) + hio_m10_si_cacls => this%hvars(ih_m10_si_cacls)%r82d, & + hio_m12_si_scls => this%hvars(ih_m12_si_scls)%r82d) ! Break up associates for NAG compilers associate(hio_c13disc_si_scpf => this%hvars(ih_c13disc_si_scpf)%r82d, & @@ -3198,8 +3268,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_promotion_rate_si_scls => this%hvars(ih_promotion_rate_si_scls)%r82d, & hio_trimming_canopy_si_scls => this%hvars(ih_trimming_canopy_si_scls)%r82d, & hio_trimming_understory_si_scls => this%hvars(ih_trimming_understory_si_scls)%r82d, & - hio_crown_area_canopy_si_scls => this%hvars(ih_crown_area_canopy_si_scls)%r82d, & - hio_crown_area_understory_si_scls => this%hvars(ih_crown_area_understory_si_scls)%r82d, & + hio_crown_fracarea_canopy_si_scls => this%hvars(ih_crown_fracarea_canopy_si_scls)%r82d, & + hio_crown_fracarea_understory_si_scls => this%hvars(ih_crown_fracarea_understory_si_scls)%r82d, & hio_leaf_md_canopy_si_scls => this%hvars(ih_leaf_md_canopy_si_scls)%r82d, & hio_root_md_canopy_si_scls => this%hvars(ih_root_md_canopy_si_scls)%r82d, & hio_carbon_balance_canopy_si_scls => this%hvars(ih_carbon_balance_canopy_si_scls)%r82d, & @@ -3226,34 +3296,19 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_npp_dead_understory_si_scls => this%hvars(ih_npp_dead_understory_si_scls)%r82d, & hio_npp_seed_understory_si_scls => this%hvars(ih_npp_seed_understory_si_scls)%r82d, & hio_npp_stor_understory_si_scls => this%hvars(ih_npp_stor_understory_si_scls)%r82d, & - hio_nplant_si_scagpft => this%hvars(ih_nplant_si_scagpft)%r82d, & - hio_npp_si_agepft => this%hvars(ih_npp_si_agepft)%r82d, & - hio_biomass_si_agepft => this%hvars(ih_biomass_si_agepft)%r82d, & - hio_scorch_height_si_agepft => this%hvars(ih_scorch_height_si_agepft)%r82d, & hio_yesterdaycanopylevel_canopy_si_scls => this%hvars(ih_yesterdaycanopylevel_canopy_si_scls)%r82d, & hio_yesterdaycanopylevel_understory_si_scls => this%hvars(ih_yesterdaycanopylevel_understory_si_scls)%r82d, & - hio_area_si_age => this%hvars(ih_area_si_age)%r82d, & - hio_lai_si_age => this%hvars(ih_lai_si_age)%r82d, & - hio_canopy_area_si_age => this%hvars(ih_canopy_area_si_age)%r82d, & - hio_ncl_si_age => this%hvars(ih_ncl_si_age)%r82d, & - hio_npatches_si_age => this%hvars(ih_npatches_si_age)%r82d, & - hio_zstar_si_age => this%hvars(ih_zstar_si_age)%r82d, & - hio_biomass_si_age => this%hvars(ih_biomass_si_age)%r82d, & - hio_npp_si_age => this%hvars(ih_npp_si_age)%r82d, & + hio_fracarea_si => this%hvars(ih_fracarea_si)%r81d, & + hio_canopy_fracarea_si => this%hvars(ih_canopy_fracarea_si)%r81d, & + hio_agesince_anthrodist_si => this%hvars(ih_agesince_anthrodist_si)%r81d, & + hio_primarylands_fracarea_si => this%hvars(ih_primarylands_fracarea_si)%r81d, & + hio_secondarylands_fracarea_si => this%hvars(ih_secondarylands_fracarea_si)%r81d, & + hio_fracarea_si_landuse => this%hvars(ih_fracarea_si_landuse)%r82d, & hio_npp_si_landuse => this%hvars(ih_npp_si_landuse)%r82d, & - hio_agesince_anthrodist_si_age => this%hvars(ih_agesince_anthrodist_si_age)%r82d, & - hio_secondarylands_area_si_age => this%hvars(ih_secondarylands_area_si_age)%r82d, & - hio_primarylands_area_si_age => this%hvars(ih_primarylands_area_si_age)%r82d, & - hio_area_si_landuse => this%hvars(ih_area_si_landuse)%r82d, & hio_biomass_si_landuse => this%hvars(ih_biomass_si_landuse)%r82d, & hio_burnedarea_si_landuse => this%hvars(ih_burnedarea_si_landuse)%r82d, & - hio_area_burnt_si_age => this%hvars(ih_area_burnt_si_age)%r82d, & - ! hio_fire_rate_of_spread_front_si_age => this%hvars(ih_fire_rate_of_spread_front_si_age)%r82d, & - hio_fire_intensity_si_age => this%hvars(ih_fire_intensity_si_age)%r82d, & - hio_fire_sum_fuel_si_age => this%hvars(ih_fire_sum_fuel_si_age)%r82d, & hio_burnt_frac_litter_si_fuel => this%hvars(ih_burnt_frac_litter_si_fuel)%r82d, & hio_fuel_amount_si_fuel => this%hvars(ih_fuel_amount_si_fuel)%r82d, & - hio_fuel_amount_age_fuel => this%hvars(ih_fuel_amount_age_fuel)%r82d, & hio_canopy_height_dist_si_height => this%hvars(ih_canopy_height_dist_si_height)%r82d, & hio_leaf_height_dist_si_height => this%hvars(ih_leaf_height_dist_si_height)%r82d, & hio_litter_moisture_si_fuel => this%hvars(ih_litter_moisture_si_fuel)%r82d, & @@ -3264,14 +3319,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_cwd_ag_out_si_cwdsc => this%hvars(ih_cwd_ag_out_si_cwdsc)%r82d, & hio_cwd_bg_out_si_cwdsc => this%hvars(ih_cwd_bg_out_si_cwdsc)%r82d, & hio_crownarea_si_cnlf => this%hvars(ih_crownarea_si_cnlf)%r82d, & - hio_crownarea_cl => this%hvars(ih_crownarea_cl)%r82d, & - hio_nplant_si_scag => this%hvars(ih_nplant_si_scag)%r82d, & - hio_nplant_canopy_si_scag => this%hvars(ih_nplant_canopy_si_scag)%r82d, & - hio_nplant_understory_si_scag => this%hvars(ih_nplant_understory_si_scag)%r82d, & - hio_ddbh_canopy_si_scag => this%hvars(ih_ddbh_canopy_si_scag)%r82d, & - hio_ddbh_understory_si_scag => this%hvars(ih_ddbh_understory_si_scag)%r82d, & - hio_mortality_canopy_si_scag => this%hvars(ih_mortality_canopy_si_scag)%r82d, & - hio_mortality_understory_si_scag => this%hvars(ih_mortality_understory_si_scag)%r82d ) + hio_crownarea_cl => this%hvars(ih_crownarea_cl)%r82d) ! Break up associates for NAG compilers associate( hio_site_dstatus_si_pft => this%hvars(ih_site_dstatus_si_pft)%r82d, & @@ -3285,12 +3333,10 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_seedling_pool_si_pft => this%hvars(ih_seedling_pool_si_pft)%r82d, & hio_seeds_in_si_pft => this%hvars(ih_seeds_in_si_pft)%r82d, & hio_seeds_in_local_si_pft => this%hvars(ih_seeds_in_local_si_pft)%r82d, & - hio_nplant_si_scag => this%hvars(ih_nplant_si_scag)%r82d, & - hio_nplant_canopy_si_scag => this%hvars(ih_nplant_canopy_si_scag)%r82d, & - hio_nplant_understory_si_scag => this%hvars(ih_nplant_understory_si_scag)%r82d, & hio_disturbance_rate_si_lulu => this%hvars(ih_disturbance_rate_si_lulu)%r82d, & hio_cstarvmortality_continuous_carbonflux_si_pft => this%hvars(ih_cstarvmortality_continuous_carbonflux_si_pft)%r82d, & hio_transition_matrix_si_lulu => this%hvars(ih_transition_matrix_si_lulu)%r82d, & + hio_scorch_height_si_pft => this%hvars(ih_scorch_height_si_pft)%r82d, & hio_sapwood_area_scpf => this%hvars(ih_sapwood_area_scpf)%r82d) model_day_int = nint(hlm_model_day) @@ -3383,71 +3429,45 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) cpatch => sites(s)%oldest_patch patchloop: do while(associated(cpatch)) - - cpatch%age_class = get_age_class_index(cpatch%age) - - ! Increment the fractional area in each age class bin - hio_area_si_age(io_si,cpatch%age_class) = hio_area_si_age(io_si,cpatch%age_class) & + hio_fracarea_si(io_si) = hio_fracarea_si(io_si) & + cpatch%area * AREA_INV ! ignore land use info on nocomp bareground (where landuse label = 0) if (cpatch%land_use_label .gt. nocomp_bareground_land) then - hio_area_si_landuse(io_si, cpatch%land_use_label) = & - hio_area_si_landuse(io_si, cpatch%land_use_label) & + hio_fracarea_si_landuse(io_si, cpatch%land_use_label) = & + hio_fracarea_si_landuse(io_si, cpatch%land_use_label) & + cpatch%area * AREA_INV hio_burnedarea_si_landuse(io_si, cpatch%land_use_label) = & hio_burnedarea_si_landuse(io_si, cpatch%land_use_label) + & cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day end if - - ! Increment some patch-age-resolved diagnostics - hio_lai_si_age(io_si,cpatch%age_class) = hio_lai_si_age(io_si,cpatch%age_class) & - + sum(cpatch%tlai_profile(:,:,:) * cpatch%canopy_area_profile(:,:,:) ) * cpatch%total_canopy_area - - hio_ncl_si_age(io_si,cpatch%age_class) = hio_ncl_si_age(io_si,cpatch%age_class) & - + cpatch%ncl_p * cpatch%area - - hio_npatches_si_age(io_si,cpatch%age_class) = hio_npatches_si_age(io_si,cpatch%age_class) + 1._r8 - - - - if ( ED_val_comp_excln .lt. 0._r8 ) then ! only valid when "strict ppa" enabled - hio_zstar_si_age(io_si,cpatch%age_class) = hio_zstar_si_age(io_si,cpatch%age_class) & - + cpatch%zstar * cpatch%area * AREA_INV - endif ! some diagnostics on secondary forest area and its age distribution if ( cpatch%land_use_label .eq. secondaryland ) then - ageclass_since_anthrodist = get_age_class_index(cpatch%age_since_anthro_disturbance) - - hio_agesince_anthrodist_si_age(io_si,ageclass_since_anthrodist) = & - hio_agesince_anthrodist_si_age(io_si,ageclass_since_anthrodist) & - + cpatch%area * AREA_INV - - hio_secondarylands_area_si_age(io_si,cpatch%age_class) = & - hio_secondarylands_area_si_age(io_si,cpatch%age_class) & + hio_agesince_anthrodist_si(io_si) = & + hio_agesince_anthrodist_si(io_si) & + cpatch%area * AREA_INV - else if ( cpatch%land_use_label .eq. primaryland) then - hio_primarylands_area_si_age(io_si,cpatch%age_class) = & - hio_primarylands_area_si_age(io_si,cpatch%age_class) & + hio_secondarylands_fracarea_si(io_si) = & + hio_secondarylands_fracarea_si(io_si) & + cpatch%area * AREA_INV - endif + else if ( cpatch%land_use_label .eq. primaryland ) then + hio_primarylands_fracarea_si(io_si) = & + hio_primarylands_fracarea_si(io_si) & + + cpatch%area * AREA_INV + endif - ! patch-age-resolved fire variables do ft = 1,numpft - ! for scorch height, weight the value by patch area within any - ! given age class - in the event that there is more than one - ! patch per age class. - iagepft = cpatch%age_class + (ft-1) * nlevage - hio_scorch_height_si_agepft(io_si,iagepft) = hio_scorch_height_si_agepft(io_si,iagepft) + & - cpatch%Scorch_ht(ft) * cpatch%area + hio_scorch_height_si_pft(io_si,ft) = hio_scorch_height_si_pft(io_si,ft) + & + cpatch%Scorch_ht(ft) * cpatch%area * AREA_INV + ! weight the value by patch area within any given age class - in the event that + ! there is more than one patch per age class - ! and also pft-labeled patch areas in the event that we are in nocomp mode if ( hlm_use_nocomp .eq. itrue .and. cpatch%nocomp_pft_label .eq. ft) then this%hvars(ih_nocomp_pftpatchfraction_si_pft)%r82d(io_si,ft) = & @@ -3463,23 +3483,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) end do - ! fractional area burnt [frac/day] -> [frac/sec] - hio_area_burnt_si_age(io_si,cpatch%age_class) = hio_area_burnt_si_age(io_si,cpatch%age_class) + & - cpatch%frac_burnt * cpatch%area * AREA_INV / sec_per_day - - ! hio_fire_rate_of_spread_front_si_age(io_si, cpatch%age_class) = hio_fire_rate_of_spread_si_age(io_si, cpatch%age_class) + & - ! cpatch%ros_front * cpatch*frac_burnt * cpatch%area * AREA_INV - - ! Fire intensity weighted by burned fraction [kJ/m/s] -> [J/m/s] - hio_fire_intensity_si_age(io_si, cpatch%age_class) = hio_fire_intensity_si_age(io_si, cpatch%age_class) + & - cpatch%FI * cpatch%frac_burnt * cpatch%area * AREA_INV * J_per_kJ - - ! Fuel sum [kg/m2] - hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) = hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) + & - cpatch%fuel%non_trunk_loading * cpatch%area * AREA_INV - - - ! loop through cohorts on patch ccohort => cpatch%shortest cohortloop: do while(associated(ccohort)) @@ -3496,8 +3499,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) n_perm2 = ccohort%n * AREA_INV - hio_canopy_area_si_age(io_si,cpatch%age_class) = hio_canopy_area_si_age(io_si,cpatch%age_class) & - + ccohort%c_area * AREA_INV + hio_canopy_fracarea_si(io_si) = hio_canopy_fracarea_si(io_si) + ccohort%c_area * AREA_INV ! calculate leaf height distribution, assuming leaf area is evenly distributed thru crown depth call CrownDepth(ccohort%height,ft,crown_depth) @@ -3538,14 +3540,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! Mass pools [kg] elloop: do el = 1, num_elements - sapw_m = ccohort%prt%GetState(sapw_organ, element_list(el)) - struct_m = ccohort%prt%GetState(struct_organ, element_list(el)) - leaf_m = ccohort%prt%GetState(leaf_organ, element_list(el)) - fnrt_m = ccohort%prt%GetState(fnrt_organ, element_list(el)) - store_m = ccohort%prt%GetState(store_organ, element_list(el)) - repro_m = ccohort%prt%GetState(repro_organ, element_list(el)) - alive_m = leaf_m + fnrt_m + sapw_m - total_m = alive_m + store_m + struct_m + call ccohort%prt%GetBiomass(element_list(el), & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) i_scpf = ccohort%size_by_pft_class @@ -3582,10 +3578,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_biomass_si_pft(io_si, ft) = hio_biomass_si_pft(io_si, ft) + & (ccohort%n * AREA_INV) * total_m - ! update total biomass per age bin - hio_biomass_si_age(io_si,cpatch%age_class) = hio_biomass_si_age(io_si,cpatch%age_class) & - + total_m * ccohort%n * AREA_INV - ! biomass by land use type hio_biomass_si_landuse(io_si, cpatch%land_use_label) = & hio_biomass_si_landuse(io_si, cpatch%land_use_label) & @@ -3825,11 +3817,9 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) icdpf = get_cdamagesizepft_class_index(ccohort%dbh, ccohort%crowndamage, ccohort%pft) this%hvars(ih_mortality_si_cdpf)%r82d(io_si,icdpf) = & - this%hvars(ih_mortality_si_cdpf)%r82d(io_si,icdpf) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + ccohort%frmort + & - ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + this%hvars(ih_mortality_si_cdpf)%r82d(io_si,icdpf) + & + ccohort%SumMortForHistory(per_year = .true.) * & + ccohort%n / m2_per_ha ! crown damage by size by pft this%hvars(ih_nplant_si_cdpf)%r82d(io_si, icdpf) = & @@ -3853,20 +3843,11 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) end if ! Carbon only metrics - sapw_m = ccohort%prt%GetState(sapw_organ, carbon12_element) - struct_m = ccohort%prt%GetState(struct_organ, carbon12_element) - leaf_m = ccohort%prt%GetState(leaf_organ, carbon12_element) - fnrt_m = ccohort%prt%GetState(fnrt_organ, carbon12_element) - store_m = ccohort%prt%GetState(store_organ, carbon12_element) - repro_m = ccohort%prt%GetState(repro_organ, carbon12_element) - alive_m = leaf_m + fnrt_m + sapw_m - total_m = alive_m + store_m + struct_m + call ccohort%prt%GetBiomass(carbon12_element, & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) hio_mortality_carbonflux_si_pft(io_si,ccohort%pft) = hio_mortality_carbonflux_si_pft(io_si,ccohort%pft) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * & - total_m * ccohort%n * days_per_sec * years_per_day * ha_per_m2 + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * total_m * & + ccohort%SumMortForHistory(per_year = .false.) * total_m * & ccohort%n * ha_per_m2 @@ -3909,42 +3890,12 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_biomass_si_scls(io_si,scls) = hio_biomass_si_scls(io_si,scls) + & total_m * ccohort%n * AREA_INV - ! age-resolved cohort-based areas - - hio_npp_si_age(io_si,cpatch%age_class) = hio_npp_si_age(io_si,cpatch%age_class) + & - ccohort%n * ccohort%npp_acc_hold * AREA_INV / days_per_year / sec_per_day - - ! update size-class x patch-age related quantities - - iscag = get_sizeage_class_index(ccohort%dbh,cpatch%age) - - hio_nplant_si_scag(io_si,iscag) = hio_nplant_si_scag(io_si,iscag) + ccohort%n / m2_per_ha + ! update size-class quantities hio_nplant_si_scls(io_si,scls) = hio_nplant_si_scls(io_si,scls) + ccohort%n / m2_per_ha - - ! update size, age, and PFT - indexed quantities - iscagpft = get_sizeagepft_class_index(ccohort%dbh,cpatch%age,ccohort%pft) - - hio_nplant_si_scagpft(io_si,iscagpft) = hio_nplant_si_scagpft(io_si,iscagpft) + ccohort%n / m2_per_ha - - ! update age and PFT - indexed quantities - iagepft = get_agepft_class_index(cpatch%age,ccohort%pft) - - hio_npp_si_agepft(io_si,iagepft) = hio_npp_si_agepft(io_si,iagepft) + & - ccohort%n * ccohort%npp_acc_hold * AREA_INV / days_per_year / sec_per_day - - hio_biomass_si_agepft(io_si,iagepft) = hio_biomass_si_agepft(io_si,iagepft) + & - total_m * ccohort%n * AREA_INV - ! update SCPF/SCLS- and canopy/subcanopy- partitioned quantities canlayer: if (ccohort%canopy_layer .eq. 1) then - hio_nplant_canopy_si_scag(io_si,iscag) = hio_nplant_canopy_si_scag(io_si,iscag) + ccohort%n / m2_per_ha - hio_mortality_canopy_si_scag(io_si,iscag) = hio_mortality_canopy_si_scag(io_si,iscag) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha - hio_ddbh_canopy_si_scag(io_si,iscag) = hio_ddbh_canopy_si_scag(io_si,iscag) + & - ccohort%ddbhdt*ccohort%n * m_per_cm / m2_per_ha hio_bstor_canopy_si_scpf(io_si,scpf) = hio_bstor_canopy_si_scpf(io_si,scpf) + & store_m * ccohort%n / m2_per_ha hio_bleaf_canopy_si_scpf(io_si,scpf) = hio_bleaf_canopy_si_scpf(io_si,scpf) + & @@ -3959,10 +3910,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! ccohort%frmort + ccohort%smort + ccohort%asmort) * ccohort%n hio_mortality_canopy_si_scpf(io_si,scpf) = hio_mortality_canopy_si_scpf(io_si,scpf)+ & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + ccohort%frmort + & - ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha hio_m3_mortality_canopy_si_scpf(io_si,scpf) = hio_m3_mortality_canopy_si_scpf(io_si,scpf) + & ccohort%cmort * ccohort%n / m2_per_ha @@ -3975,7 +3923,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ccohort%treesai*ccohort%c_area * AREA_INV hio_trimming_canopy_si_scls(io_si,scls) = hio_trimming_canopy_si_scls(io_si,scls) + & ccohort%n * ccohort%canopy_trim / m2_per_ha - hio_crown_area_canopy_si_scls(io_si,scls) = hio_crown_area_canopy_si_scls(io_si,scls) + & + hio_crown_fracarea_canopy_si_scls(io_si,scls) = hio_crown_fracarea_canopy_si_scls(io_si,scls) + & ccohort%c_area * AREA_INV hio_gpp_canopy_si_scpf(io_si,scpf) = hio_gpp_canopy_si_scpf(io_si,scpf) + & n_perm2*ccohort%gpp_acc_hold / days_per_year / sec_per_day @@ -3990,10 +3938,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! sum of all mortality hio_mortality_canopy_si_scls(io_si,scls) = hio_mortality_canopy_si_scls(io_si,scls) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha hio_m3_mortality_canopy_si_scls(io_si,scls) = hio_m3_mortality_canopy_si_scls(io_si,scls) + & ccohort%cmort * ccohort%n / m2_per_ha @@ -4016,10 +3961,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf)+ & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + ccohort%frmort + ccohort%smort + & - ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha ! nplants by damage this%hvars(ih_nplant_canopy_si_cdpf)%r82d(io_si,icdpf) = & @@ -4066,12 +4008,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) else canlayer - hio_nplant_understory_si_scag(io_si,iscag) = hio_nplant_understory_si_scag(io_si,iscag) + ccohort%n / m2_per_ha - hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha - hio_ddbh_understory_si_scag(io_si,iscag) = hio_ddbh_understory_si_scag(io_si,iscag) + & - ccohort%ddbhdt*ccohort%n * m_per_cm / m2_per_ha hio_bstor_understory_si_scpf(io_si,scpf) = hio_bstor_understory_si_scpf(io_si,scpf) + & store_m * ccohort%n / m2_per_ha hio_bleaf_understory_si_scpf(io_si,scpf) = hio_bleaf_understory_si_scpf(io_si,scpf) + & @@ -4087,10 +4023,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! ccohort%frmort + ccohort%smort + ccohort%asmort) * ccohort%n hio_mortality_understory_si_scpf(io_si,scpf) = hio_mortality_understory_si_scpf(io_si,scpf)+ & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha hio_m3_mortality_understory_si_scpf(io_si,scpf) = hio_m3_mortality_understory_si_scpf(io_si,scpf) + & ccohort%cmort * ccohort%n / m2_per_ha @@ -4111,7 +4044,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ccohort%treelai*ccohort%c_area * AREA_INV hio_trimming_understory_si_scls(io_si,scls) = hio_trimming_understory_si_scls(io_si,scls) + & ccohort%n * ccohort%canopy_trim / m2_per_ha - hio_crown_area_understory_si_scls(io_si,scls) = hio_crown_area_understory_si_scls(io_si,scls) + & + hio_crown_fracarea_understory_si_scls(io_si,scls) = hio_crown_fracarea_understory_si_scls(io_si,scls) + & ccohort%c_area * AREA_INV hio_gpp_understory_si_scpf(io_si,scpf) = hio_gpp_understory_si_scpf(io_si,scpf) + & n_perm2*ccohort%gpp_acc_hold / days_per_year / sec_per_day @@ -4127,10 +4060,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! sum of all mortality hio_mortality_understory_si_scls(io_si,scls) = hio_mortality_understory_si_scls(io_si,scls) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + & - ccohort%frmort + ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha hio_m3_mortality_understory_si_scls(io_si,scls) = hio_m3_mortality_understory_si_scls(io_si,scls) + & ccohort%cmort * ccohort%n / m2_per_ha @@ -4156,10 +4086,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ! total mortality of understory cohorts by damage x size x pft this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) + & - (ccohort%bmort + ccohort%hmort + ccohort%cmort + ccohort%frmort + & - ccohort%smort + ccohort%asmort + ccohort%dgmort) * ccohort%n / m2_per_ha + & - (ccohort%lmort_direct + ccohort%lmort_collateral + ccohort%lmort_infra) * & - ccohort%n * sec_per_day * days_per_year / m2_per_ha + ccohort%SumMortForHistory(per_year = .true.) * ccohort%n / m2_per_ha this%hvars(ih_nplant_understory_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_nplant_understory_si_cdpf)%r82d(io_si,icdpf) + & @@ -4251,10 +4178,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) do i_fuel = 1, num_fuel_classes - i_agefuel = get_agefuel_class_index(cpatch%age,i_fuel) - hio_fuel_amount_age_fuel(io_si,i_agefuel) = hio_fuel_amount_age_fuel(io_si,i_agefuel) + & - cpatch%fuel%frac_loading(i_fuel) * cpatch%fuel%non_trunk_loading * cpatch%area * AREA_INV - hio_litter_moisture_si_fuel(io_si, i_fuel) = hio_litter_moisture_si_fuel(io_si, i_fuel) + & cpatch%fuel%effective_moisture(i_fuel) * cpatch%area * AREA_INV @@ -4316,28 +4239,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) cpatch => cpatch%younger end do patchloop !patch loop - - - - ! divide so-far-just-summed but to-be-averaged patch-age-class - ! variables by patch-age-class area to get mean values - do ipa2 = 1, nlevage - if (hio_area_si_age(io_si, ipa2) .gt. nearzero) then - hio_lai_si_age(io_si, ipa2) = hio_lai_si_age(io_si, ipa2) / (hio_area_si_age(io_si, ipa2)*AREA) - hio_ncl_si_age(io_si, ipa2) = hio_ncl_si_age(io_si, ipa2) / (hio_area_si_age(io_si, ipa2)*AREA) - do ft = 1, numpft - iagepft = ipa2 + (ft-1) * nlevage - hio_scorch_height_si_agepft(io_si, iagepft) = & - hio_scorch_height_si_agepft(io_si, iagepft) / (hio_area_si_age(io_si, ipa2)*AREA) - enddo - else - hio_lai_si_age(io_si, ipa2) = 0._r8 - hio_ncl_si_age(io_si, ipa2) = 0._r8 - endif - end do - - - ! pass the cohort termination mortality as a flux to the history, and then reset the termination mortality buffer ! note there are various ways of reporting the total mortality, so pass to these as well do i_pft = 1, numpft @@ -4425,19 +4326,25 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_mortality_understory_si_scls(io_si,i_scls) = hio_mortality_understory_si_scls(io_si,i_scls) + & sites(s)%imort_rate(i_scls, ft) / m2_per_ha ! - iscag = i_scls ! since imort is by definition something that only happens in newly disturbed patches, treat as such - hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & - sites(s)%imort_rate(i_scls, ft) / m2_per_ha - ! fire mortality from the site-level diagnostic rates - hio_m5_si_scpf(io_si,i_scpf) = (sites(s)%fmort_rate_canopy(i_scls, ft) + & - sites(s)%fmort_rate_ustory(i_scls, ft)) / m2_per_ha + ! wildfire mortality from the site-level diagnostic rates + hio_m5_si_scpf(io_si,i_scpf) = (sites(s)%nonrx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%nonrx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha hio_m5_si_scls(io_si,i_scls) = hio_m5_si_scls(io_si,i_scls) + & - (sites(s)%fmort_rate_canopy(i_scls, ft) + & - sites(s)%fmort_rate_ustory(i_scls, ft)) / m2_per_ha - ! - hio_crownfiremort_si_scpf(io_si,i_scpf) = sites(s)%fmort_rate_crown(i_scls, ft) / m2_per_ha - hio_cambialfiremort_si_scpf(io_si,i_scpf) = sites(s)%fmort_rate_cambial(i_scls, ft) / m2_per_ha + (sites(s)%nonrx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%nonrx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha + ! prescribed fire mortality + hio_m12_si_scpf(io_si,i_scpf) = (sites(s)%rx_fmort_rate_canopy(i_scls,ft) + & + sites(s)%rx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha + hio_m12_si_scls(io_si,i_scls) = hio_m12_si_scls(io_si,i_scls) + & + (sites(s)%rx_fmort_rate_canopy(i_scls, ft) + & + sites(s)%rx_fmort_rate_ustory(i_scls, ft)) / m2_per_ha + ! wildfire crown and cambial mort + hio_nonrx_crown_mort_si_scpf(io_si,i_scpf) = sites(s)%nonrx_fmort_rate_crown(i_scls, ft) / m2_per_ha + hio_nonrx_cambial_mort_si_scpf(io_si,i_scpf) = sites(s)%nonrx_fmort_rate_cambial(i_scls, ft) / m2_per_ha + ! prescribed fire crown and cambial mort + hio_rx_crown_mort_si_scpf(io_si,i_scpf) = sites(s)%rx_fmort_rate_crown(i_scls, ft) / m2_per_ha + hio_rx_cambial_mort_si_scpf(io_si,i_scpf) = sites(s)%rx_fmort_rate_cambial(i_scls, ft) / m2_per_ha ! ! fire components of overall canopy and understory mortality hio_mortality_canopy_si_scpf(io_si,i_scpf) = hio_mortality_canopy_si_scpf(io_si,i_scpf) + & @@ -4453,14 +4360,6 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_mortality_understory_si_scls(io_si,i_scls) = hio_mortality_understory_si_scls(io_si,i_scls) + & sites(s)%fmort_rate_ustory(i_scls, ft) / m2_per_ha - ! - ! for scag variables, also treat as happening in the newly-disurbed patch - - hio_mortality_canopy_si_scag(io_si,iscag) = hio_mortality_canopy_si_scag(io_si,iscag) + & - sites(s)%fmort_rate_canopy(i_scls, ft) / m2_per_ha - hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & - sites(s)%fmort_rate_ustory(i_scls, ft) / m2_per_ha - ! while in this loop, pass the fusion-induced growth rate flux to history hio_growthflux_fusion_si_scpf(io_si,i_scpf) = hio_growthflux_fusion_si_scpf(io_si,i_scpf) + & sites(s)%growthflux_fusion(i_scls, ft) * days_per_year / m2_per_ha @@ -4473,7 +4372,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) do ft = 1, numpft hio_mortality_carbonflux_si_pft(io_si,ft) = hio_mortality_carbonflux_si_pft(io_si,ft) + & (sites(s)%fmort_carbonflux_canopy(ft) + & - sites(s)%fmort_carbonflux_ustory(ft) ) / g_per_kg + & + sites(s)%fmort_carbonflux_ustory(ft)) / g_per_kg + & sites(s)%imort_carbonflux(ft) + & sum(sites(s)%term_carbonflux_ustory(:,ft)) * days_per_sec * ha_per_m2 + & sum(sites(s)%term_carbonflux_canopy(:,ft)) * days_per_sec * ha_per_m2 @@ -4510,48 +4409,23 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) (sites(s)%term_nindivs_ustory_damage(icdam, i_scls, ft) * days_per_year) + & sites(s)%imort_rate_damage(icdam, i_scls, ft) + & sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft) + & - sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft) ) / m2_per_ha + sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft)) / m2_per_ha this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_canopy_si_cdpf)%r82d(io_si,icdpf) + & ( sites(s)%term_nindivs_canopy_damage(icdam,i_scls,ft) * days_per_year + & - sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft) )/ m2_per_ha + sites(s)%fmort_rate_canopy_damage(icdam, i_scls, ft))/ m2_per_ha this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) = & this%hvars(ih_mortality_understory_si_cdpf)%r82d(io_si,icdpf) + & ( sites(s)%term_nindivs_ustory_damage(icdam, i_scls,ft) * days_per_year + & sites(s)%imort_rate_damage(icdam, i_scls, ft) + & - sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft) )/ m2_per_ha + sites(s)%fmort_rate_ustory_damage(icdam, i_scls, ft))/ m2_per_ha end do end do end do end if - sites(s)%term_nindivs_canopy(:,:,:) = 0._r8 - sites(s)%term_nindivs_ustory(:,:,:) = 0._r8 - sites(s)%imort_carbonflux(:) = 0._r8 - sites(s)%imort_rate(:,:) = 0._r8 - sites(s)%fmort_rate_canopy(:,:) = 0._r8 - sites(s)%fmort_rate_ustory(:,:) = 0._r8 - sites(s)%fmort_carbonflux_canopy(:) = 0._r8 - sites(s)%fmort_carbonflux_ustory(:) = 0._r8 - sites(s)%fmort_rate_cambial(:,:) = 0._r8 - sites(s)%fmort_rate_crown(:,:) = 0._r8 - sites(s)%growthflux_fusion(:,:) = 0._r8 - sites(s)%fmort_abg_flux(:,:) = 0._r8 - sites(s)%imort_abg_flux(:,:) = 0._r8 - sites(s)%term_abg_flux(:,:) = 0._r8 - - sites(s)%imort_rate_damage(:,:,:) = 0.0_r8 - sites(s)%term_nindivs_canopy_damage(:,:,:) = 0.0_r8 - sites(s)%term_nindivs_ustory_damage(:,:,:) = 0.0_r8 - sites(s)%imort_cflux_damage(:,:) = 0._r8 - sites(s)%term_cflux_canopy_damage(:,:) = 0._r8 - sites(s)%term_cflux_ustory_damage(:,:) = 0._r8 - sites(s)%fmort_rate_canopy_damage(:,:,:) = 0._r8 - sites(s)%fmort_rate_ustory_damage(:,:,:) = 0._r8 - sites(s)%fmort_cflux_canopy_damage(:,:) = 0._r8 - sites(s)%fmort_cflux_ustory_damage(:,:) = 0._r8 ! pass the recruitment rate as a flux to the history, and then reset the recruitment buffer do ft = 1, numpft @@ -4579,7 +4453,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) hio_m7_si_scpf(io_si,i_scpf) + & hio_m8_si_scpf(io_si,i_scpf) + & hio_m9_si_scpf(io_si,i_scpf) + & - hio_m10_si_scpf(io_si,i_scpf) + hio_m10_si_scpf(io_si,i_scpf) + & + hio_m12_si_scpf(io_si,i_scpf) if(hlm_use_tree_damage .eq. itrue) then hio_mortality_si_pft(io_si, ft) = hio_mortality_si_pft(io_si,ft) + & @@ -4645,7 +4520,7 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) litt => cpatch%litter(el) - area_frac = cpatch%area * AREA_INV + patch_fracarea = cpatch%area * AREA_INV ! Sum up all output fluxes (fragmentation) hio_litter_out_elem(io_si,el) = hio_litter_out_elem(io_si,el) + & @@ -4697,13 +4572,8 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) ccohort => cpatch%tallest do while(associated(ccohort)) - sapw_m = ccohort%prt%GetState(sapw_organ, element_list(el)) - struct_m = ccohort%prt%GetState(struct_organ, element_list(el)) - leaf_m = ccohort%prt%GetState(leaf_organ, element_list(el)) - fnrt_m = ccohort%prt%GetState(fnrt_organ, element_list(el)) - store_m = ccohort%prt%GetState(store_organ, element_list(el)) - repro_m = ccohort%prt%GetState(repro_organ, element_list(el)) - total_m = sapw_m+struct_m+leaf_m+fnrt_m+store_m+repro_m + call ccohort%prt%GetBiomass(element_list(el), & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) i_scpf = ccohort%size_by_pft_class @@ -4873,7 +4743,362 @@ subroutine update_history_dyn2(this,nc,nsites,sites,bc_in) end associate return - end subroutine update_history_dyn2 + end subroutine update_history_dyn_subsite + + ! ========================================================================================= + + subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites) + + ! --------------------------------------------------------------------------------- + ! This subroutine is intended to update all history variables with upfreq == + ! group_dyna_complx that have a dimension in addition to that for the site level + ! which DO include age class. So, eg., FATES_VEGC_APPF is updated here, + ! but not FATES_VEGC or FATES_VEGC_PF. + ! --------------------------------------------------------------------------------- + + ! Arguments + class(fates_history_interface_type) :: this + integer , intent(in) :: nc ! clump index + integer , intent(in) :: nsites + type(ed_site_type) , intent(inout), target :: sites(nsites) + + type(fates_cohort_type), pointer :: ccohort + type(fates_patch_type), pointer :: cpatch + integer :: s, ft, iagepft, i_agefuel, iscag, iscagpft, i_fuel, i_scls, io_si + integer :: iscag_anthrodist ! what is the equivalent age class for + ! time-since-anthropogenic-disturbance of secondary forest + real(r8) :: mort + real(r8) :: sapw_m ! Sapwood mass (elemental, c,n or p) [kg/plant] + real(r8) :: struct_m ! Structural mass "" + real(r8) :: leaf_m ! Leaf mass "" + real(r8) :: fnrt_m ! Fineroot mass "" + real(r8) :: store_m ! Storage mass "" + real(r8) :: alive_m ! Alive biomass (sap+leaf+fineroot+repro+storage) "" + real(r8) :: total_m ! Total vegetation mass + real(r8) :: repro_m ! Total reproductive mass (on plant) "" + real(r8) :: patch_area_div_site_area ! Weighting based on patch area relative to site area + real(r8) :: patch_canarea_div_site_area ! Weighting based on patch canopy area relative to site area + real(r8) :: cohort_n_div_site_area ! Weighting based on cohort density relative to site area + + associate( & + hio_lai_si_age => this%hvars(ih_lai_si_age)%r82d, & + hio_ncl_si_age => this%hvars(ih_ncl_si_age)%r82d, & + hio_scorch_height_si_agepft => this%hvars(ih_scorch_height_si_agepft)%r82d, & + hio_zstar_si_age => this%hvars(ih_zstar_si_age)%r82d, & + hio_fracarea_burnt_si_age => this%hvars(ih_fracarea_burnt_si_age)%r82d, & + hio_rx_fracarea_burnt_si_age => this%hvars(ih_rx_fracarea_burnt_si_age)%r82d, & + hio_nonrx_fracarea_burnt_si_age => this%hvars(ih_nonrx_fracarea_burnt_si_age)%r82d, & + hio_fire_sum_fuel_si_age => this%hvars(ih_fire_sum_fuel_si_age)%r82d, & + hio_fuel_amount_si_agfc => this%hvars(ih_fuel_amount_si_agfc)%r82d, & +! hio_fire_rate_of_spread_front_si_age => this%hvars(ih_fire_rate_of_spread_front_si_age)%r82d, & + hio_mortality_canopy_si_scag => this%hvars(ih_mortality_canopy_si_scag)%r82d, & + hio_mortality_understory_si_scag => this%hvars(ih_mortality_understory_si_scag)%r82d, & + hio_biomass_si_age => this%hvars(ih_biomass_si_age)%r82d, & + hio_biomass_si_agepft => this%hvars(ih_biomass_si_agepft)%r82d, & + hio_npp_si_age => this%hvars(ih_npp_si_age)%r82d, & + hio_npp_si_agepft => this%hvars(ih_npp_si_agepft)%r82d, & + hio_ddbh_canopy_si_scag => this%hvars(ih_ddbh_canopy_si_scag)%r82d, & + hio_fire_intensity_si_age => this%hvars(ih_fire_intensity_si_age)%r82d, & + hio_rx_intensity_si_age => this%hvars(ih_rx_intensity_si_age)%r82d, & + hio_nonrx_intensity_si_age => this%hvars(ih_nonrx_intensity_si_age)%r82d, & + hio_npatches_si_age => this%hvars(ih_npatches_si_age)%r82d, & + hio_canopy_fracarea_si_age => this%hvars(ih_canopy_fracarea_si_age)%r82d, & + hio_nplant_si_scag => this%hvars(ih_nplant_si_scag)%r82d, & + hio_nplant_si_scagpft => this%hvars(ih_nplant_si_scagpft)%r82d, & + hio_nplant_canopy_si_scag => this%hvars(ih_nplant_canopy_si_scag)%r82d, & + hio_nplant_understory_si_scag => this%hvars(ih_nplant_understory_si_scag)%r82d, & + hio_fracarea_si_age => this%hvars(ih_fracarea_si_age)%r82d, & + hio_agesince_anthrodist_si_age => this%hvars(ih_agesince_anthrodist_si_age)%r82d, & + hio_primarylands_fracarea_si_age => this%hvars(ih_primarylands_fracarea_si_age)%r82d, & + hio_secondarylands_fracarea_si_age => this%hvars(ih_secondarylands_fracarea_si_age)%r82d, & + hio_ddbh_understory_si_scag => this%hvars(ih_ddbh_understory_si_scag)%r82d) + + siteloop: do s = 1,nsites + io_si = sites(s)%h_gid + + ! Loop through patches to sum up diagnostics + cpatch => sites(s)%oldest_patch + patchloop: do while(associated(cpatch)) + cpatch%age_class = get_age_class_index(cpatch%age) + patch_area_div_site_area = cpatch%area * AREA_INV + patch_canarea_div_site_area = cpatch%total_canopy_area * AREA_INV + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!! Weighting by (or using) patch area relative to total site area !!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! Increment the fractional area in each age class bin + hio_fracarea_si_age(io_si,cpatch%age_class) = hio_fracarea_si_age(io_si,cpatch%age_class) & + + cpatch%area * AREA_INV + + do ft = 1,numpft + iagepft = get_agepft_class_index(cpatch%age,ft) + hio_scorch_height_si_agepft(io_si,iagepft) = hio_scorch_height_si_agepft(io_si,iagepft) + & + cpatch%Scorch_ht(ft) * patch_area_div_site_area + end do + + hio_ncl_si_age(io_si,cpatch%age_class) = hio_ncl_si_age(io_si,cpatch%age_class) & + + cpatch%ncl_p * patch_area_div_site_area + + hio_fracarea_burnt_si_age(io_si,cpatch%age_class) = hio_fracarea_burnt_si_age(io_si,cpatch%age_class) + & + cpatch%frac_burnt / sec_per_day & ! [frac/day] -> [frac/sec] + * patch_area_div_site_area + + hio_rx_fracarea_burnt_si_age(io_si,cpatch%age_class) = hio_rx_fracarea_burnt_si_age(io_si,cpatch%age_class) + & + cpatch%rx_frac_burnt / sec_per_day & ! [frac/day] -> [frac/sec] + * patch_area_div_site_area + + hio_nonrx_fracarea_burnt_si_age(io_si,cpatch%age_class) = hio_nonrx_fracarea_burnt_si_age(io_si,cpatch%age_class) + & + cpatch%nonrx_frac_burnt / sec_per_day & ! [frac/day] -> [frac/sec] + * patch_area_div_site_area + + hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) = hio_fire_sum_fuel_si_age(io_si, cpatch%age_class) + & + cpatch%fuel%non_trunk_loading * patch_area_div_site_area + do i_fuel = 1,num_fuel_classes + i_agefuel = get_agefuel_class_index(cpatch%age,i_fuel) + hio_fuel_amount_si_agfc(io_si,i_agefuel) = hio_fuel_amount_si_agfc(io_si,i_agefuel) + & + cpatch%fuel%frac_loading(i_fuel) * cpatch%fuel%non_trunk_loading * patch_area_div_site_area + end do + + ! only valid when "strict ppa" enabled + if ( comp_excln_exp .lt. 0._r8 ) then + hio_zstar_si_age(io_si,cpatch%age_class) = hio_zstar_si_age(io_si,cpatch%age_class) & + + cpatch%zstar * patch_area_div_site_area + end if + + ! some diagnostics on secondary forest area and its age distribution + if ( cpatch%land_use_label .eq. secondaryland ) then + + iscag_anthrodist = get_age_class_index(cpatch%age_since_anthro_disturbance) + + hio_agesince_anthrodist_si_age(io_si,iscag_anthrodist) = & + hio_agesince_anthrodist_si_age(io_si,iscag_anthrodist) & + + patch_area_div_site_area + + hio_secondarylands_fracarea_si_age(io_si,cpatch%age_class) = & + hio_secondarylands_fracarea_si_age(io_si,cpatch%age_class) & + + patch_area_div_site_area + else if ( cpatch%land_use_label .eq. primaryland) then + hio_primarylands_fracarea_si_age(io_si,cpatch%age_class) = & + hio_primarylands_fracarea_si_age(io_si,cpatch%age_class) & + + patch_area_div_site_area + endif + + !!!!!!!!!!!!!!!!!!!!!!! + !!! Other weighting !!! + !!!!!!!!!!!!!!!!!!!!!!! + + ! LAI is weighted by patch canopy area relative to total site area---NOT site CANOPY + ! area---because bare ground is included in LAI calculation. + hio_lai_si_age(io_si,cpatch%age_class) = hio_lai_si_age(io_si,cpatch%age_class) & + + sum(cpatch%tlai_profile(:,:,:) * cpatch%canopy_area_profile(:,:,:) ) & + * patch_canarea_div_site_area + + ! These fire variables are intended to be weighted by fire area. However, for precision + ! reasons, we don't divide by site-wide burned area here. Instead, in the long_name of + ! the history file variable, we tell the users to do that division themselves. + hio_fire_intensity_si_age(io_si, cpatch%age_class) = hio_fire_intensity_si_age(io_si,cpatch%age_class) + & + cpatch%FI * J_per_kJ & ! [kJ/m/s] -> [J/m/s] + * cpatch%frac_burnt * patch_area_div_site_area + ! hio_fire_rate_of_spread_front_si_age(io_si, cpatch%age_class) = hio_fire_rate_of_spread_si_age(io_si, cpatch%age_class) + & + ! cpatch%ros_front * cpatch*frac_burnt * patch_area_div_site_area + + hio_rx_intensity_si_age(io_si, cpatch%age_class) = hio_rx_intensity_si_age(io_si, cpatch%age_class) + & + cpatch%rx_FI * J_per_kJ & ! [kJ/m/s] -> [J/m/s] + * cpatch%rx_frac_burnt * patch_area_div_site_area + + hio_nonrx_intensity_si_age(io_si, cpatch%age_class) = hio_nonrx_intensity_si_age(io_si, cpatch%age_class) + & + cpatch%nonrx_FI * J_per_kJ & ! [kJ/m/s] -> [J/m/s] + * cpatch%nonrx_frac_burnt * patch_area_div_site_area + + ! Weighted by cohort canopy area relative to site area + ccohort => cpatch%shortest + cohortloop: do while(associated(ccohort)) + cohort_n_div_site_area = ccohort%n * AREA_INV + + hio_canopy_fracarea_si_age(io_si,cpatch%age_class) = hio_canopy_fracarea_si_age(io_si,cpatch%age_class) & + + ccohort%c_area * AREA_INV + + notnew: if( .not.(ccohort%isnew) ) then + hio_npp_si_age(io_si,cpatch%age_class) = hio_npp_si_age(io_si,cpatch%age_class) & + + ccohort%npp_acc_hold / days_per_year / sec_per_day & + * cohort_n_div_site_area + end if notnew + + ccohort => ccohort%taller + end do cohortloop + + ! Not weighted + hio_npatches_si_age(io_si,cpatch%age_class) = hio_npatches_si_age(io_si,cpatch%age_class) + 1._r8 + + cpatch => cpatch%younger + end do patchloop + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !!! Weighting by cohort stem density relative to total site area !!! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! Loop through patches to sum up diagnostics + cpatch => sites(s)%oldest_patch + patchloop2: do while(associated(cpatch)) + cpatch%age_class = get_age_class_index(cpatch%age) + + ! Loop through cohorts on patch + ccohort => cpatch%shortest + cohortloop2: do while(associated(ccohort)) + + ! If you SUM across all age classes, you should get the mean site value. + cohort_n_div_site_area = ccohort%n * AREA_INV + + iscag = get_sizeage_class_index(ccohort%dbh, cpatch%age) + iagepft = get_agepft_class_index(cpatch%age,ccohort%pft) + iscagpft = get_sizeagepft_class_index(ccohort%dbh,cpatch%age,ccohort%pft) + + ! Biomass + call ccohort%prt%GetBiomass(carbon12_element, & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) + hio_biomass_si_age(io_si,cpatch%age_class) = hio_biomass_si_age(io_si,cpatch%age_class) & + + total_m * cohort_n_div_site_area + hio_biomass_si_agepft(io_si,iagepft) = hio_biomass_si_agepft(io_si,iagepft) & + + total_m * cohort_n_div_site_area + + if (.not. (ccohort%isnew)) then + hio_npp_si_agepft(io_si,iagepft) = hio_npp_si_agepft(io_si,iagepft) + & + ccohort%npp_acc_hold / days_per_year / sec_per_day & ! [kgC/indiv/yr] -> [kgC/s] + * cohort_n_div_site_area + hio_nplant_si_scag(io_si,iscag) = hio_nplant_si_scag(io_si,iscag) + cohort_n_div_site_area + hio_nplant_si_scagpft(io_si,iscagpft) = hio_nplant_si_scagpft(io_si,iscagpft) + cohort_n_div_site_area + + ! Canopy vs. understory variables + mort = ccohort%SumMortForHistory(per_year = .true.) + if (ccohort%canopy_layer .eq. 1) then + hio_mortality_canopy_si_scag(io_si,iscag) = hio_mortality_canopy_si_scag(io_si,iscag) + & + mort * cohort_n_div_site_area + hio_ddbh_canopy_si_scag(io_si,iscag) = hio_ddbh_canopy_si_scag(io_si,iscag) + & + ccohort%ddbhdt * m_per_cm & ! [m] -> [cm] + * cohort_n_div_site_area + hio_nplant_canopy_si_scag(io_si,iscag) = hio_nplant_canopy_si_scag(io_si,iscag) + cohort_n_div_site_area + else + hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si, iscag) + & + mort * cohort_n_div_site_area + hio_ddbh_understory_si_scag(io_si,iscag) = hio_ddbh_understory_si_scag(io_si,iscag) + & + ccohort%ddbhdt * m_per_cm & ! [m] -> [cm] + * cohort_n_div_site_area + hio_nplant_understory_si_scag(io_si,iscag) = hio_nplant_understory_si_scag(io_si,iscag) + cohort_n_div_site_area + end if ! canopy layer? + end if ! cohort is new? + + ccohort => ccohort%taller + end do cohortloop2 + + cpatch => cpatch%younger + end do patchloop2 + + ! The mortality components in this loop already include cohort density (cohort%n), so they + ! don't use cohort_n_div_site_area. + do ft = 1, numpft + do i_scls = 1,nlevsclass + + ! since imort and fmort by definition something only happens in newly disturbed patches, + ! treat as such + iscag = i_scls + + ! add imort to other mortality terms. consider imort as understory mortality even if it happens in + ! cohorts that may have been promoted as part of the patch creation, and use the pre-calculated site-level + ! values to avoid biasing the results by the dramatically-reduced number densities in cohorts that are subject to imort + hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & + sites(s)%imort_rate(i_scls, ft) * AREA_INV + + ! add fire mortality to other mortality terms + hio_mortality_canopy_si_scag(io_si,iscag) = hio_mortality_canopy_si_scag(io_si,iscag) + & + sites(s)%fmort_rate_canopy(i_scls, ft) * AREA_INV + hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & + sites(s)%fmort_rate_ustory(i_scls, ft) * AREA_INV + + ! add termination mortality to other mortality terms + hio_mortality_canopy_si_scag(io_si,iscag) = hio_mortality_canopy_si_scag(io_si,iscag) + & + sum(sites(s)%term_nindivs_canopy(:,i_scls,ft)) * days_per_year * AREA_INV + hio_mortality_understory_si_scag(io_si,iscag) = hio_mortality_understory_si_scag(io_si,iscag) + & + sum(sites(s)%term_nindivs_ustory(:,i_scls,ft)) * days_per_year * AREA_INV + + end do ! size class loop + end do ! pft loop + end do siteloop + + end associate + end subroutine update_history_dyn_subsite_ageclass + + ! =============================================================================================== + + subroutine reset_history_dyn_subsite(this, nsites, sites) + + ! ------------------------------------------------------------------------------------ + ! This resets some variables that need to be zeroed out after dyn2 history subroutines + ! ------------------------------------------------------------------------------------ + ! + ! Arguments + class(fates_history_interface_type) :: this + integer , intent(in) :: nsites + type(ed_site_type), intent(inout), target :: sites(nsites) + ! + ! Local variables + integer :: s + + siteloop: do s = 1,nsites + + sites(s)%term_nindivs_canopy(:,:,:) = 0._r8 + sites(s)%term_nindivs_ustory(:,:,:) = 0._r8 + sites(s)%imort_carbonflux(:) = 0._r8 + sites(s)%imort_rate(:,:) = 0._r8 + sites(s)%fmort_rate_canopy(:,:) = 0._r8 + sites(s)%fmort_rate_ustory(:,:) = 0._r8 + sites(s)%fmort_carbonflux_canopy(:) = 0._r8 + sites(s)%fmort_carbonflux_ustory(:) = 0._r8 + sites(s)%fmort_rate_cambial(:,:) = 0._r8 + sites(s)%fmort_rate_crown(:,:) = 0._r8 + + sites(s)%nonrx_fmort_rate_canopy(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_ustory(:,:) = 0._r8 + sites(s)%nonrx_fmort_carbonflux_canopy(:) = 0._r8 + sites(s)%nonrx_fmort_carbonflux_ustory(:) = 0._r8 + sites(s)%nonrx_fmort_rate_cambial(:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_crown(:,:) = 0._r8 + sites(s)%nonrx_fmort_abg_flux(:,:) = 0._r8 + sites(s)%rx_fmort_rate_canopy(:,:) = 0._r8 + sites(s)%rx_fmort_rate_ustory(:,:) = 0._r8 + sites(s)%rx_fmort_carbonflux_canopy(:) = 0._r8 + sites(s)%rx_fmort_carbonflux_ustory(:) = 0._r8 + sites(s)%rx_fmort_rate_cambial(:,:) = 0._r8 + sites(s)%rx_fmort_rate_crown(:,:) = 0._r8 + sites(s)%rx_fmort_abg_flux(:,:) = 0._r8 + + sites(s)%growthflux_fusion(:,:) = 0._r8 + sites(s)%fmort_abg_flux(:,:) = 0._r8 + sites(s)%imort_abg_flux(:,:) = 0._r8 + sites(s)%term_abg_flux(:,:) = 0._r8 + + sites(s)%imort_rate_damage(:,:,:) = 0.0_r8 + sites(s)%term_nindivs_canopy_damage(:,:,:) = 0.0_r8 + sites(s)%term_nindivs_ustory_damage(:,:,:) = 0.0_r8 + sites(s)%imort_cflux_damage(:,:) = 0._r8 + sites(s)%term_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%term_cflux_ustory_damage(:,:) = 0._r8 + sites(s)%fmort_rate_canopy_damage(:,:,:) = 0._r8 + sites(s)%fmort_rate_ustory_damage(:,:,:) = 0._r8 + sites(s)%fmort_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%fmort_cflux_ustory_damage(:,:) = 0._r8 + + sites(s)%nonrx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + sites(s)%nonrx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + sites(s)%nonrx_fmort_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%nonrx_fmort_cflux_ustory_damage(:,:) = 0._r8 + sites(s)%rx_fmort_rate_canopy_damage(:,:,:) = 0._r8 + sites(s)%rx_fmort_rate_ustory_damage(:,:,:) = 0._r8 + sites(s)%rx_fmort_cflux_canopy_damage(:,:) = 0._r8 + sites(s)%rx_fmort_cflux_ustory_damage(:,:) = 0._r8 + + end do siteloop + end subroutine reset_history_dyn_subsite ! =============================================================================================== @@ -4896,9 +5121,10 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) real(r8) , intent(in) :: dt_tstep if(hlm_hist_level_hifrq>0) then - call update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + call update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,dt_tstep) if(hlm_hist_level_hifrq>1) then - call update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + call update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) + call update_history_hifrq_subsite_ageclass(this,nsites,sites,dt_tstep) end if end if @@ -4906,7 +5132,13 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) return end subroutine update_history_hifrq - subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,dt_tstep) + + ! --------------------------------------------------------------------------------- + ! This subroutine is intended to update all history variables with upfreq == + ! group_hifrq_simple: i.e., those that are saved at the site level. So, eg., + ! FATES_GPP is updated here, but not FATES_GPP_AP. + ! --------------------------------------------------------------------------------- ! ! Arguments @@ -4915,7 +5147,6 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) type(bc_in_type) , intent(in) :: bc_in(nsites) - type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep ! Locals @@ -5009,6 +5240,7 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) hio_nir_rad_err_si(io_si) = 0._r8 cpatch => sites(s)%oldest_patch do while(associated(cpatch)) + if( abs(cpatch%rad_error(ivis)-hlm_hio_ignore_val)>nearzero ) then hio_vis_rad_err_si(io_si) = hio_vis_rad_err_si(io_si) + & @@ -5123,16 +5355,18 @@ subroutine update_history_hifrq1(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) end associate return - end subroutine update_history_hifrq1 + end subroutine update_history_hifrq_sitelevel ! =============================================================================================== - subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) + subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) ! --------------------------------------------------------------------------------- - ! This is the call to update the history IO arrays for multi-dimension arrays - ! that change rapidly. This is an expensive call, the model will probably run - ! much faster if the user is not using any of these diagnostics. + ! This subroutine is intended to update all history variables with upfreq == + ! group_hifrq_complex (i.e., that have a dimension in addition to that for the site + ! level) that do NOT include age class. So, e.g., FATES_GPP_PF would be updated + ! here, but not FATES_GPP or FATES_GPP_AP. This is an expensive call; the model + ! will probably run much faster if the user is not using any of these diagnostics. ! --------------------------------------------------------------------------------- ! @@ -5141,8 +5375,6 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites type(ed_site_type) , intent(inout), target :: sites(nsites) - type(bc_in_type) , intent(in) :: bc_in(nsites) - type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep ! Locals @@ -5153,8 +5385,6 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) integer :: ft ! functional type index real(r8) :: n_density ! individual of cohort per m2. real(r8) :: n_perm2 ! individuals per m2 for the whole column - real(r8) :: patch_area_by_age(nlevage) ! patch area in each bin for normalizing purposes - real(r8) :: canopy_area_by_age(nlevage) ! canopy area in each bin for normalizing purposes real(r8) :: site_area_veg_inv ! 1/area of the site that is not bare-ground integer :: ipa2 ! patch incrementer integer :: clllpf_indx, cnlf_indx, ipft, ican, ileaf ! more iterators and indices @@ -5186,10 +5416,7 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) hio_froot_mr_understory_si_scls => this%hvars(ih_froot_mr_understory_si_scls)%r82d, & hio_resp_g_understory_si_scls => this%hvars(ih_resp_g_understory_si_scls)%r82d, & hio_resp_m_understory_si_scls => this%hvars(ih_resp_m_understory_si_scls)%r82d, & - hio_gpp_si_age => this%hvars(ih_gpp_si_age)%r82d, & hio_gpp_si_landuse => this%hvars(ih_gpp_si_landuse)%r82d, & - hio_c_stomata_si_age => this%hvars(ih_c_stomata_si_age)%r82d, & - hio_c_lblayer_si_age => this%hvars(ih_c_lblayer_si_age)%r82d, & hio_parsun_z_si_cnlf => this%hvars(ih_parsun_z_si_cnlf)%r82d, & hio_parsha_z_si_cnlf => this%hvars(ih_parsha_z_si_cnlf)%r82d, & hio_ts_net_uptake_si_cnlf => this%hvars(ih_ts_net_uptake_si_cnlf)%r82d, & @@ -5234,27 +5461,9 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) io_si = sites(s)%h_gid - patch_area_by_age(1:nlevage) = 0._r8 - canopy_area_by_age(1:nlevage) = 0._r8 - cpatch => sites(s)%oldest_patch do while(associated(cpatch)) - patch_area_by_age(cpatch%age_class) = & - patch_area_by_age(cpatch%age_class) + cpatch%area - - canopy_area_by_age(cpatch%age_class) = & - canopy_area_by_age(cpatch%age_class) + cpatch%total_canopy_area - - ! Canopy resitance terms - hio_c_stomata_si_age(io_si,cpatch%age_class) = & - hio_c_stomata_si_age(io_si,cpatch%age_class) + & - cpatch%c_stomata * cpatch%total_canopy_area * mol_per_umol - - hio_c_lblayer_si_age(io_si,cpatch%age_class) = & - hio_c_lblayer_si_age(io_si,cpatch%age_class) + & - cpatch%c_lblayer * cpatch%total_canopy_area * mol_per_umol - ccohort => cpatch%shortest do while(associated(ccohort)) @@ -5296,10 +5505,6 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) hio_ar_frootm_si_scpf(io_si,scpf) = hio_ar_frootm_si_scpf(io_si,scpf) + & ccohort%froot_mr * n_perm2 - ! accumulate fluxes per patch age bin - hio_gpp_si_age(io_si,cpatch%age_class) = hio_gpp_si_age(io_si,cpatch%age_class) & - + ccohort%gpp_tstep * ccohort%n * dt_tstep_inv - if (cpatch%land_use_label .gt. nocomp_bareground_land) then hio_gpp_si_landuse(io_si,cpatch%land_use_label) = hio_gpp_si_landuse(io_si,cpatch%land_use_label) & + ccohort%gpp_tstep * ccohort%n * dt_tstep_inv @@ -5339,7 +5544,6 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) endif end associate endif - ! canopy leaf carbon balance ican = ccohort%canopy_layer do ileaf=1,ccohort%nv @@ -5491,7 +5695,8 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) cl_area = cl_area + hio_crownfrac_clllpf(io_si,clllpf_indx) ! Convert from total m2 to fraction of the site - hio_crownfrac_clllpf(io_si,clllpf_indx) = hio_crownfrac_clllpf(io_si,clllpf_indx)*site_area_veg_inv + hio_crownfrac_clllpf(io_si,clllpf_indx) = & + hio_crownfrac_clllpf(io_si,clllpf_indx)*site_area_veg_inv end if end do do_ipft2 @@ -5540,37 +5745,103 @@ subroutine update_history_hifrq2(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) end do do_ican2 end if if_zenith2 - + enddo do_sites ! site loop - ! Normalize age stratified diagnostics - ! ---------------------------------------------------------------- - do ipa2 = 1, nlevage - if (patch_area_by_age(ipa2) .gt. nearzero) then - hio_gpp_si_age(io_si, ipa2) = & - hio_gpp_si_age(io_si, ipa2) / (patch_area_by_age(ipa2)) - else - hio_gpp_si_age(io_si, ipa2) = 0._r8 - endif + end associate - ! Normalize resistance diagnostics - if (canopy_area_by_age(ipa2) .gt. nearzero) then - hio_c_stomata_si_age(io_si,ipa2) = & - hio_c_stomata_si_age(io_si,ipa2) / canopy_area_by_age(ipa2) + end subroutine update_history_hifrq_subsite - hio_c_lblayer_si_age(io_si,ipa2) = & - hio_c_lblayer_si_age(io_si,ipa2) / canopy_area_by_age(ipa2) - else - hio_c_stomata_si_age(io_si,ipa2) = 0._r8 - hio_c_lblayer_si_age(io_si,ipa2) = 0._r8 - end if + ! =============================================================================================== - end do + subroutine update_history_hifrq_subsite_ageclass(this,nsites,sites,dt_tstep) - enddo do_sites ! site loop + ! --------------------------------------------------------------------------------- + ! This subroutine is intended to update all history variables with upfreq == + ! group_hifrq_complex (i.e., that have a dimension in addition to that for the site + ! level) that DO include age class. So, e.g., FATES_GPP_AP is updated here, but not + ! FATES_GPP or FATES_GPP_PF. This is an expensive call; the model will probably run + ! much faster if the user is not using any of these diagnostics. + ! --------------------------------------------------------------------------------- - end associate + ! + ! Arguments + class(fates_history_interface_type) :: this + integer , intent(in) :: nsites + type(ed_site_type) , intent(inout), target :: sites(nsites) + real(r8) , intent(in) :: dt_tstep - end subroutine update_history_hifrq2 + type(fates_cohort_type), pointer :: ccohort + type(fates_patch_type), pointer :: cpatch + integer :: s, io_si + real(r8) :: site_canopy_area + real(r8) :: dt_tstep_inv ! Time step in frequency units (/s) + real(r8) :: patch_canarea_div_site_canarea ! Weighting based on patch canopy area relative to site canopy area + real(r8) :: cohort_n_div_site_area ! Weighting based on cohort density relative to site area + + associate( & + hio_c_lblayer_si_age => this%hvars(ih_c_lblayer_si_age)%r82d, & + hio_c_stomata_si_age => this%hvars(ih_c_stomata_si_age)%r82d, & + hio_gpp_si_age => this%hvars(ih_gpp_si_age)%r82d & + ) + + dt_tstep_inv = 1.0_r8 / dt_tstep + + do_sites: do s = 1,nsites + + ! Get site-wide canopy area + site_canopy_area = 0._r8 + cpatch => sites(s)%oldest_patch + do while(associated(cpatch)) + site_canopy_area = site_canopy_area + cpatch%total_canopy_area + cpatch => cpatch%younger + end do + + io_si = sites(s)%h_gid + + ! Get ageclass-stratified variables + cpatch => sites(s)%oldest_patch + do while(associated(cpatch)) + cpatch%age_class = get_age_class_index(cpatch%age) + + ! Canopy resistance terms + if (site_canopy_area .gt. nearzero) then + patch_canarea_div_site_canarea = cpatch%total_canopy_area / site_canopy_area + hio_c_stomata_si_age(io_si,cpatch%age_class) = & + hio_c_stomata_si_age(io_si,cpatch%age_class) + & + cpatch%c_stomata * mol_per_umol & + * patch_canarea_div_site_canarea + + hio_c_lblayer_si_age(io_si,cpatch%age_class) = & + hio_c_lblayer_si_age(io_si,cpatch%age_class) + & + cpatch%c_lblayer * mol_per_umol & + * patch_canarea_div_site_canarea + else + hio_c_stomata_si_age(io_si,cpatch%age_class) = 0._r8 + hio_c_lblayer_si_age(io_si,cpatch%age_class) = 0._r8 + end if + + ccohort => cpatch%shortest + do while(associated(ccohort)) + if (ccohort%isnew) then + ccohort => ccohort%taller + cycle + end if + cohort_n_div_site_area = ccohort%n * AREA_INV + + hio_gpp_si_age(io_si,cpatch%age_class) = hio_gpp_si_age(io_si,cpatch%age_class) & + + ccohort%gpp_tstep * dt_tstep_inv & + * cohort_n_div_site_area + + ccohort => ccohort%taller + end do ! cohort loop + + cpatch => cpatch%younger + end do ! patch loop + end do do_sites + + end associate + + end subroutine update_history_hifrq_subsite_ageclass ! ===================================================================================== @@ -5990,6 +6261,26 @@ end subroutine initialize_history_vars ! ==================================================================================== + function per_ageclass_norm_info(this, norm_var) + + ! --------------------------------------------------------------------------------- + ! Produces a bit of text to include in long_name of variables with age-class axes, + ! explaining how to get real per-ageclass values if needed. + ! --------------------------------------------------------------------------------- + ! + ! Arguments + class(fates_history_interface_type) :: this ! Not used, but needed for a function in this type + character(len=*), intent(in) :: norm_var ! The variable(s) that this one should be multiplied by to get actual values + ! + ! Result + character(len=fates_long_string_length) :: per_ageclass_norm_info + + per_ageclass_norm_info = "; for real per-age values, mult by " // norm_var + + end function per_ageclass_norm_info + + ! ==================================================================================== + subroutine define_history_vars(this, initialize_variables) ! --------------------------------------------------------------------------------- @@ -6099,13 +6390,13 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_AREA_PLANTS', units='m2 m-2', & long='area occupied by all plants per m2 land area', use_default='active', & avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, & - initialize=initialize_variables, index=ih_area_plant_si) + initialize=initialize_variables, index=ih_fracarea_plant_si) call this%set_history_var(vname='FATES_AREA_TREES', units='m2 m-2', & long='area occupied by woody plants per m2 land area', use_default='active', & avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_area_trees_si) + index=ih_fracarea_trees_si) call this%set_history_var(vname='FATES_FRACTION', units='m2 m-2', & long='total gridcell fraction which FATES is running over', use_default='active', & @@ -6178,12 +6469,6 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_elai_si) - call this%set_history_var(vname='FATES_WOOD_PRODUCT', units='kg m-2', & - long='total wood product from logging in kg carbon per m2 land area', & - use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & - upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_woodproduct_si) - ! Fire Variables call this%set_history_var(vname='FATES_NESTEROV_INDEX', units='', & @@ -6191,6 +6476,12 @@ subroutine define_history_vars(this, initialize_variables) avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_nesterov_fire_danger_si) + + call this%set_history_var(vname='FATES_RX_BURN_WINDOW', units='', & + long='fraction of time when prescribed fire burn window presents', & + use_default='active',avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_burn_window_si) call this%set_history_var(vname='FATES_IGNITIONS', & units='m-2 s-1', & @@ -6225,23 +6516,81 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_FIRE_INTENSITY', & units='J m-1 s-1', & - long='spitfire surface fireline intensity in J per m per second', & + long='spitfire surface fireline intensity in J per m per second, sum of rx and wildfire', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & index=ih_fire_intensity_si) call this%set_history_var(vname='FATES_FIRE_INTENSITY_BURNFRAC', & units='J m-1 s-1', & - long='product of surface fire intensity and burned area fraction -- divide by FATES_BURNFRAC to get area-weighted mean intensity', & + long='product of surface fire intensity and burned area fraction, sum of rx and wildfire-- divide by FATES_BURNFRAC to get area-weighted mean intensity', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_fire_intensity_fracarea_product_si) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY', & + units='J m-1 s-1', & + long='spitfire surface fireline intensity of wildfire in J per m per second', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_nonrx_intensity_si) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY_BURNFRAC', & + units='J m-1 s-1', & + long='product of wildfire intensity and burned fraction -- divide by FATES_WILDFIRE_BURNFRAC to get area-weighted mean intensity', & use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_fire_intensity_area_product_si) + index=ih_nonrx_intensity_fracarea_product_si) + + call this%set_history_var(vname='FATES_RXFIRE_INTENSITY', & + units='J m-1 s-1', & + long='spitfire surface fireline intensity of prescribed fire in J per m per second', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_intensity_si) + + call this%set_history_var(vname='FATES_RXFIRE_INTENSITY_BURNFRAC', & + units='J m-1 s-1', & + long='product of prescribed fire intensity and burned fraction -- to be devided by FATES_RXFIRE_BURNFRAC to get area-weighted mean intensity', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_intensity_fracarea_product_si) call this%set_history_var(vname='FATES_BURNFRAC', units='s-1', & - long='burned area fraction per second', use_default='active', & + long='totaL burned area fraction per second -- sum of rxfire and wildfire burnt frac', use_default='active', & avgflag='A', vtype=site_r8, hlms='CLM:ALM', & upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & - index=ih_fire_area_si) + index=ih_fire_fracarea_si) + + call this%set_history_var(vname='FATES_WILDFIRE_BURNFRAC', units='s-1', & + long='burned area fraction per second by wildfire', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_nonrx_fracarea_si) + + call this%set_history_var(vname='FATES_RXFIRE_BURNFRAC', units='s-1', & + long='burned area fraction per second by prescribed fire', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_fracarea_si) + + call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FUEL', units='', & + long='burnable area fraction by Rx fire when fuel cond. met', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_fracarea_fuel_si) + + call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FI', units='', & + long='burnable area fraction by Rx fire when fuel and FI cond. met', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_fracarea_fi_si) + + call this%set_history_var(vname='FATES_RXFIRE_BURNABLE_FINAL', units='', & + long='burnable area fraction by Rx fire when all cond. met', & + use_default='active', avgflag='A', vtype=site_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index=ih_rx_fracarea_final_si) call this%set_history_var(vname='FATES_FUEL_MEF', units='m3 m-3', & long='fuel moisture of extinction (volumetric)', & @@ -6754,8 +7103,25 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, index = ih_crownarea_ustory_damage_si ) end if if_crowndamage1 - + call this%set_history_var(vname='FATES_NCL', units='', & + long='number of canopy levels', & + use_default='inactive', avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_ncl_si) + + if ( comp_excln_exp .lt. 0._r8 ) then ! only valid when "strict ppa" enabled + tempstring = 'active' + else + tempstring = 'inactive' + endif + + call this%set_history_var(vname='FATES_ZSTAR', units='m', & + long='product of zstar and patch area', & + use_default=tempstring, avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_simple, ivar=ivar, initialize=initialize_variables, & + index=ih_zstar_si) + if_dyn1: if(hlm_hist_level_dynam>1) then call this%set_history_var(vname='FATES_NPP_LU', units='kg m-2 s-1', & @@ -6767,7 +7133,7 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_PATCHAREA_LU', units='m2 m-2', & long='patch area by land use type', use_default='active', & avgflag='A', vtype=site_landuse_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, & - ivar=ivar, initialize=initialize_variables, index=ih_area_si_landuse) + ivar=ivar, initialize=initialize_variables, index=ih_fracarea_si_landuse) call this%set_history_var(vname='FATES_VEGC_LU', units='kg m-2', & long='Vegetation Carbon by land use type', use_default='active', & @@ -6798,7 +7164,7 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_RECRUITMENT_CFLUX_PF', units='kg m-2 yr-1', & long='total PFT-level biomass of new recruits in kg of carbon per land area', & use_default='active', avgflag='A', vtype=site_pft_r8, hlms='CLM:ALM', & - upfreq=1, ivar=ivar, initialize=initialize_variables, & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_recruitment_cflux_si_pft) call this%set_history_var(vname='FATES_LEAFC_PF', units='kg m-2', & @@ -6962,27 +7328,40 @@ subroutine define_history_vars(this, initialize_variables) index=ih_nocomp_pftburnedarea_si_pft) endif nocomp_if + call this%set_history_var(vname='FATES_CANOPYAREA', units='m2 m-2', & + long='canopy area per m2 land area', use_default='inactive', & + avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index=ih_canopy_fracarea_si) + + call this%set_history_var(vname='FATES_PATCHAREA', units='m2 m-2', & + long='patch area per m2 land area', use_default='inactive', & + avgflag='A', vtype=site_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index=ih_fracarea_si) + ! patch age class variables call this%set_history_var(vname='FATES_PATCHAREA_AP', units='m2 m-2', & - long='patch area by age bin per m2 land area', use_default='active', & + long='patch area by age bin per m2 land area', & + use_default='active', & avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index=ih_area_si_age) + initialize=initialize_variables, index=ih_fracarea_si_age) call this%set_history_var(vname='FATES_LAI_AP', units='m2 m-2', & - long='total leaf area index by age bin per m2 land area', & + long='total leaf area index by age bin per m2 land area'// & + this%per_ageclass_norm_info('FATES_CANOPYAREA/FATES_CANOPYAREA_AP'), & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_lai_si_age) - - call this%set_history_var(vname='FATES_CANOPYAREA_AP', units='m2 m-2', & - long='canopy area by age bin per m2 land area', use_default='active', & + long='canopy area by age bin per m2 land area'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & + use_default='active', & avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index=ih_canopy_area_si_age) + initialize=initialize_variables, index=ih_canopy_fracarea_si_age) call this%set_history_var(vname='FATES_NCL_AP', units='', & - long='number of canopy levels by age bin', & + long='number of canopy levels by age bin' // & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_ncl_si_age) @@ -6993,7 +7372,7 @@ subroutine define_history_vars(this, initialize_variables) upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_npatches_si_age) - if ( ED_val_comp_excln .lt. 0._r8 ) then ! only valid when "strict ppa" enabled + if ( comp_excln_exp .lt. 0._r8 ) then ! only valid when "strict ppa" enabled tempstring = 'active' else tempstring = 'inactive' @@ -7023,26 +7402,47 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_biomass_si_age) - call this%set_history_var(vname='FATES_SECONDARY_ANTHRODISTAGE_AP', & + call this%set_history_var(vname='FATES_SECONDARY_AREA_ANTHRO_AP', & units='m2 m-2', & long='secondary forest patch area age distribution since anthropogenic disturbance', & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index=ih_agesince_anthrodist_si_age) + call this%set_history_var(vname='FATES_SECONDARY_AREA_ANTHRO', & + units='m2 m-2', & + long='secondary forest patch area since anthropgenic disturbance', & + use_default='inactive', avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index=ih_agesince_anthrodist_si) + + call this%set_history_var(vname='FATES_SECONDARY_AREA', & + units='m2 m-2', & + long='secondary forest patch area since any kind of disturbance', & + use_default='inactive', avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index=ih_secondarylands_fracarea_si) + call this%set_history_var(vname='FATES_SECONDARY_AREA_AP', & units='m2 m-2', & long='secondary forest patch area age distribution since any kind of disturbance', & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & - index=ih_secondarylands_area_si_age) + index=ih_secondarylands_fracarea_si_age) + + call this%set_history_var(vname='FATES_PRIMARY_AREA', & + units='m2 m-2', & + long='primary forest patch area since any kind of disturbance', & + use_default='inactive', avgflag='A', vtype=site_r8, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index=ih_primarylands_fracarea_si) call this%set_history_var(vname='FATES_PRIMARY_AREA_AP', & units='m2 m-2', & long='primary forest patch area age distribution since any kind of disturbance', & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & - index=ih_primarylands_area_si_age) + index=ih_primarylands_fracarea_si_age) call this%set_history_var(vname='FATES_FRAGMENTATION_SCALER_SL', units='', & long='factor (0-1) by which litter/cwd fragmentation proceeds relative to max rate by soil layer', & @@ -7066,21 +7466,47 @@ subroutine define_history_vars(this, initialize_variables) long='spitfire fuel quantity in each age x fuel class in kg carbon per m2 land area', & use_default='inactive', avgflag='A', vtype=site_agefuel_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & - index = ih_fuel_amount_age_fuel) + index = ih_fuel_amount_si_agfc) call this%set_history_var(vname='FATES_BURNFRAC_AP', units='s-1', & - long='spitfire fraction area burnt (per second) by patch age', & + long='spitfire fraction area burnt (per second) by patch age, sum of rx and wildfire', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & - index = ih_area_burnt_si_age) + index = ih_fracarea_burnt_si_age) call this%set_history_var(vname='FATES_FIRE_INTENSITY_BURNFRAC_AP', & units='J m-1 s-1', & - long='product of fire intensity and burned fraction, resolved by patch age (so divide by FATES_BURNFRAC_AP to get burned-area-weighted-average intensity)', & + long='product of fire intensity and burned fraction, sum of rx and wildfire, resolved by patch age (so divide by FATES_BURNFRAC_AP to get area-weighted mean intensity)', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & - upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & index = ih_fire_intensity_si_age) + call this%set_history_var(vname='FATES_WILDFIRE_BURNFRAC_AP', units='s-1', & + long='spitfire fraction area burnt due to wildfire by patch age', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index = ih_nonrx_fracarea_burnt_si_age) + + call this%set_history_var(vname='FATES_WILDFIRE_INTENSITY_BURNFRAC_AP', & + units='J m-1 s-1', & + long='product of wildfire intensity and burned fraction, resolved by patch age, divide by FATES_WILDFIRE_BURNFRAC_AP to get area-weighted mean intensity)', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=group_dyna_complx, ivar=ivar, initialize=initialize_variables, & + index = ih_nonrx_intensity_si_age) + + call this%set_history_var(vname='FATES_RXFIRE_BURNFRAC_AP', units='s-1', & + long='spitfire fraction area burnt due to prescribed fire by patch age', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index= ih_rx_fracarea_burnt_si_age) + + call this%set_history_var(vname='FATES_RXFIRE_INTENSITY_BURNFRAC_AP', & + units='J m-1 s-1', & + long='product of prescribed fire intensity and burned fraction by patch age, to be devided by FATES_RXFIRE_BURNFRAC_AP to get area-weighted mean intensity)', & + use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + upfreq=1, ivar=ivar, initialize=initialize_variables, & + index = ih_rx_intensity_si_age) + call this%set_history_var(vname='FATES_FUEL_AMOUNT_AP', units='kg m-2', & long='spitfire ground fuel (kg carbon per m2) related to FATES_ROS (omits 1000hr fuels) within each patch age bin (divide by FATES_PATCHAREA_AP to get fuel per unit area of that-age patch)', & use_default='active', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & @@ -7392,20 +7818,23 @@ subroutine define_history_vars(this, initialize_variables) ! size class by age dimensioned variables call this%set_history_var(vname='FATES_NPLANT_SZAP', units = 'm-2', & - long='number of plants per m2 in each size x age class', & + long='number of plants per m2, per size x age class'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_scag_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_nplant_si_scag) call this%set_history_var(vname='FATES_NPLANT_CANOPY_SZAP', units = 'm-2', & - long='number of plants per m2 in canopy in each size x age class', & + long='number of canopy plants per m2, per size x age class'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_scag_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_nplant_canopy_si_scag) call this%set_history_var(vname='FATES_NPLANT_USTORY_SZAP', & units = 'm-2', & - long='number of plants per m2 in understory in each size x age class', & + long='number of understory plants per m2 understory, per size x age class'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_scag_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_nplant_understory_si_scag) @@ -7441,7 +7870,8 @@ subroutine define_history_vars(this, initialize_variables) ! size x age x pft dimensioned call this%set_history_var(vname='FATES_NPLANT_SZAPPF',units = 'm-2', & - long='number of plants per m2 in each size x age x pft class', & + long='number of plants per m2, per size x age x pft class'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_scagpft_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_nplant_si_scagpft) @@ -7460,17 +7890,25 @@ subroutine define_history_vars(this, initialize_variables) index = ih_npp_si_age) call this%set_history_var(vname='FATES_VEGC_APPF',units = 'kg m-2', & - long='biomass per PFT in each age bin in kg carbon per m2', & + long='biomass per PFT in each age bin in kg carbon per m2'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_agepft_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_biomass_si_agepft) call this%set_history_var(vname='FATES_SCORCH_HEIGHT_APPF',units = 'm', & - long='SPITFIRE flame Scorch Height (calculated per PFT in each patch age bin)', & + long='SPITFIRE flame Scorch Height (calculated per PFT in each patch age bin)'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_agepft_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_scorch_height_si_agepft) + call this%set_history_var(vname='FATES_SCORCH_HEIGHT_PF',units = 'm', & + long='SPITFIRE flame Scorch Height (calculated per PFT)', & + use_default='inactive', avgflag='A', vtype=site_pft_r8, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index = ih_scorch_height_si_pft) + ! Carbon Flux (grid dimension x scpf) (THESE ARE DEFAULT INACTIVE!!! ! (BECAUSE THEY TAKE UP SPACE!!! @@ -7660,26 +8098,47 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_m4_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_FIRE_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_SZPF', & units = 'm-2 yr-1', & - long='fire mortality by pft/size in number of plants per m2 per year', & + long='wildfire mortality by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_m5_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_CROWNSCORCH_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_CROWN_SZPF', & units = 'm-2 yr-1', & - long='fire mortality from crown scorch by pft/size in number of plants per m2 per year', & + long='wildfire mortality from crown scorch by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_crownfiremort_si_scpf) + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index = ih_nonrx_crown_mort_si_scpf) - call this%set_history_var(vname='FATES_MORTALITY_CAMBIALBURN_SZPF', & + call this%set_history_var(vname='FATES_MORTALITY_WILDFIRE_CAMBIAL_SZPF', & units = 'm-2 yr-1', & - long='fire mortality from cambial burn by pft/size in number of plants per m2 per year', & + long='wildfire mortality from cambial burn by pft/size in number of plants per m2 per year', & use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & - hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_cambialfiremort_si_scpf) + hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & + initialize=initialize_variables, index = ih_nonrx_cambial_mort_si_scpf) + + call this%set_history_var(vname='FATES_MORTALITY_RXFIRE_SZPF', & + units = 'm-2 yr-1', & + long='prescribed fire mortality by pft/size in number of plants per m2 per year', & + use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & + hlms='CLM:ALM', upfreq=1, ivar=ivar, & + initialize=initialize_variables, index = ih_m12_si_scpf) + + call this%set_history_var(vname='FATES_MORTALITY_RXCROWN_SZPF', & + units = 'm-2 yr-1', & + long='fire mortality from crown scorch due to prescribed fire by pft/size in number of plants per m2 per year', & + use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & + hlms='CLM:ALM', upfreq=1, ivar=ivar, & + initialize=initialize_variables, index = ih_rx_crown_mort_si_scpf) + + call this%set_history_var(vname='FATES_MORTALITY_RXCAMBIAL_SZPF', & + units = 'm-2 yr-1', & + long='fire mortality from cambial kill due to prescribed fire by pft/size in number of plants per m2 per year', & + use_default='inactive', avgflag='A', vtype=site_size_pft_r8, & + hlms='CLM:ALM', upfreq=1, ivar=ivar, & + initialize=initialize_variables, index = ih_rx_cambial_mort_si_scpf) call this%set_history_var(vname='FATES_MORTALITY_TERMINATION_SZPF', & units = 'm-2 yr-1', & @@ -8046,6 +8505,13 @@ subroutine define_history_vars(this, initialize_variables) hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & initialize=initialize_variables, index = ih_m5_si_scls) + call this%set_history_var(vname='FATES_MORTALITY_RXFIRE_SZ', & + units = 'm-2 yr-1', & + long='prescribed fire mortality by size in number of plants per m2 per year', & + use_default='active', avgflag='A', vtype=site_size_r8, & + hlms='CLM:ALM', upfreq=1, ivar=ivar, & + initialize=initialize_variables, index = ih_m12_si_scls) + call this%set_history_var(vname='FATES_MORTALITY_TERMINATION_SZ', & units = 'm-2 yr-1', & long='termination mortality (excluding C-starvation) by size in number of plants per m2 per year', & @@ -8128,13 +8594,13 @@ subroutine define_history_vars(this, initialize_variables) long='total crown area of canopy plants by size class', & use_default='inactive', avgflag='A', vtype=site_size_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_crown_area_canopy_si_scls) + initialize=initialize_variables, index = ih_crown_fracarea_canopy_si_scls) call this%set_history_var(vname='FATES_CROWNAREA_USTORY_SZ', units = 'm2 m-2', & long='total crown area of understory plants by size class', & use_default='inactive', avgflag='A', vtype=site_size_r8, & hlms='CLM:ALM', upfreq=group_dyna_complx, ivar=ivar, & - initialize=initialize_variables, index = ih_crown_area_understory_si_scls) + initialize=initialize_variables, index = ih_crown_fracarea_understory_si_scls) call this%set_history_var(vname='FATES_LEAFCTURN_CANOPY_SZ', & units = 'kg m-2 s-1', & @@ -8494,8 +8960,6 @@ subroutine define_history_vars(this, initialize_variables) end if if_dyn1 end if if_dyn0 - !HERE - if_hifrq0: if(hlm_hist_level_hifrq>0) then @@ -8673,7 +9137,8 @@ subroutine define_history_vars(this, initialize_variables) ! to save time (and some space) call this%set_history_var(vname='FATES_GPP_AP', units='kg m-2 s-1', & - long='gross primary productivity by age bin in kg carbon per m2 per second', & + long='gross primary productivity by age bin in kg carbon per m2 per second'// & + this%per_ageclass_norm_info('FATES_PATCHAREA/FATES_PATCHAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_hifr_complx, ivar=ivar, initialize=initialize_variables, & index = ih_gpp_si_age) @@ -8936,14 +9401,16 @@ subroutine define_history_vars(this, initialize_variables) call this%set_history_var(vname='FATES_LBLAYER_COND_AP', & units='mol m-2 s-1', & - long='mean leaf boundary layer conductance - by patch age', & + long='mean leaf boundary layer conductance - by patch age'// & + this%per_ageclass_norm_info('FATES_CANOPYAREA/FATES_CANOPYAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_hifr_complx, ivar=ivar, & initialize=initialize_variables, index = ih_c_lblayer_si_age) ! Canopy resistance call this%set_history_var(vname='FATES_STOMATAL_COND_AP', & - units='mol m-2 s-1', long='mean stomatal conductance - by patch age', & + units='mol m-2 s-1', long='mean stomatal conductance - by patch age'//& + this%per_ageclass_norm_info('FATES_CANOPYAREA/FATES_CANOPYAREA_AP'), & use_default='inactive', avgflag='A', vtype=site_age_r8, & hlms='CLM:ALM', upfreq=group_hifr_complx, ivar=ivar, initialize=initialize_variables, & index = ih_c_stomata_si_age) diff --git a/main/FatesHistoryVariableType.F90 b/main/FatesHistoryVariableType.F90 index b24bb1bf86..3b0bcc1b29 100644 --- a/main/FatesHistoryVariableType.F90 +++ b/main/FatesHistoryVariableType.F90 @@ -1,6 +1,7 @@ module FatesHistoryVariableType use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : fates_long_string_length use FatesGlobals, only : fates_log use FatesGlobals , only : endrun => fates_endrun use FatesIODimensionsMod, only : fates_io_dimension_type @@ -34,7 +35,7 @@ module FatesHistoryVariableType type, public :: fates_history_variable_type character(len=40) :: vname character(len=24) :: units - character(len=128) :: long + character(len=fates_long_string_length) :: long character(len=24) :: use_default ! States whether a variable should be turned ! on the output files by default (active/inactive) ! It is a good idea to set inactive for very large diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 9f6563b61a..3560aa84bb 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -113,7 +113,7 @@ module FatesInterfaceMod use FatesHydraulicsMemMod , only : nlevsoi_hyd_max use FatesTwoStreamUtilsMod, only : TransferRadParams use LeafBiophysicsMod , only : lb_params - + use LeafBiophysicsMod , only : FvCB1980 ! CIME Globals use shr_log_mod , only : errMsg => shr_log_errMsg use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=) @@ -159,7 +159,7 @@ module FatesInterfaceMod ! instance is fine. type(bc_pconst_type) :: bc_pconst - + end type fates_interface_type @@ -369,7 +369,14 @@ subroutine zero_bcs(fates,s) write(fates_log(), *) 'hlm_parteh_mode: ',hlm_parteh_mode call endrun(msg=errMsg(sourcefile, __LINE__)) end select - + + ! carbon loss to atmosphere pathways + ! (these values are a unit conversion off of the + ! equivalent "site_mass%" diagnostics, so they are not + ! incremented but set during update_site()) + fates%bc_out(s)%grazing_closs_to_atm_si = nan + fates%bc_out(s)%fire_closs_to_atm_si = nan + fates%bc_out(s)%rssun_pa(:) = 0.0_r8 fates%bc_out(s)%rssha_pa(:) = 0.0_r8 @@ -412,6 +419,10 @@ subroutine zero_bcs(fates,s) fates%bc_in(s)%hlm_luh_transitions(:) = 0.0_r8 end if + fates%bc_out(s)%veg_c_si = 0.0_r8 + fates%bc_out(s)%litter_cwd_c_si = 0.0_r8 + fates%bc_out(s)%seed_c_si = 0.0_r8 + return end subroutine zero_bcs @@ -952,9 +963,10 @@ subroutine SetFatesGlobalElements2(use_fates) call endrun(msg=errMsg(sourcefile, __LINE__)) end if - ! lower edges of VAI bins - do i = 1,nlevleaf - dlower_vai(i) = sum(dinc_vai(1:i)) + ! lower edges of VAI bins + dlower_vai(1) = 0._r8 + do i = 2,nlevleaf + dlower_vai(i) = dlower_vai(i-1) + dinc_vai(i-1) end do ! Identify number of size and age class bins for history output @@ -1489,21 +1501,22 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) hlm_sf_scalar_lightning_def = unset_int hlm_sf_successful_ignitions_def = unset_int hlm_sf_anthro_ignitions_def = unset_int + hlm_use_managed_fire = unset_int hlm_use_planthydro = unset_int hlm_use_lu_harvest = unset_int hlm_num_lu_harvest_cats = unset_int hlm_num_luh2_states = unset_int hlm_num_luh2_transitions = unset_int hlm_use_cohort_age_tracking = unset_int - hlm_daylength_factor_switch = unset_int - hlm_photo_tempsens_model = unset_int - hlm_stomatal_assim_model = unset_int - hlm_stomatal_model = unset_int + lb_params%dayl_switch = unset_int + lb_params%photo_tempsens_model = unset_int + lb_params%stomatal_assim_model = unset_int + lb_params%stomatal_model = unset_int hlm_hydr_solver = unset_int hlm_maintresp_leaf_model = unset_int hlm_mort_cstarvation_model = unset_int hlm_radiation_model = unset_int - hlm_electron_transport_model = unset_int + lb_params%electron_transport_model = unset_int !FvCB1980 ! Temporary until API Has this switch hlm_regeneration_model = unset_int hlm_use_logging = unset_int hlm_use_ed_st3 = unset_int @@ -1757,6 +1770,11 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) call endrun(msg=errMsg(sourcefile, __LINE__)) end if + if(hlm_use_managed_fire .eq. unset_int) then + write(fates_log(), *) 'switch for managed fire mode unset: hlm_use_managed_fire, exiting' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + if(trim(hlm_name).eq.'CLM' .and. hlm_parteh_mode .eq. 2) then if( sum(abs(EDPftvarcon_inst%prescribed_puptake(:)))1=external data sources (lightning and/or anthropogenic) + integer, public :: hlm_use_managed_fire ! Flag to enable managed fire mode. Requires spitfire to be on. + integer, public :: hlm_use_lu_harvest ! This flag signals whether or not to use ! harvest data from the hlm ! 0 = do not use lu harvest from hlm @@ -158,9 +160,6 @@ module FatesInterfaceTypesMod integer, public :: hlm_use_tree_damage ! This flag signals whether or not to turn on the ! tree damage module - integer, public :: hlm_daylength_factor_switch ! This switch enables the use of the daylength factor from the HLM - ! 1 = TRUE, 0 = FALSE - integer, public :: hlm_hydr_solver ! Switch that defines which hydraulic solver to use ! 1 = Taylor solution that solves plant fluxes with 1 layer ! sequentially placing solution on top of previous layer solves @@ -169,16 +168,6 @@ module FatesInterfaceTypesMod ! 3 = Newton-Raphson (Deprecated) solution that solves all fluxes in a plant and ! the soil simultaneously, 2D: soil x (root + shell) - integer, public :: hlm_photo_tempsens_model ! switch for choosing the model that defines the temperature - ! sensitivity of photosynthetic parameters (vcmax, jmax). - ! 0=non-acclimating, 1=Kumarathunge et al., 2019 - - integer, public :: hlm_stomatal_assim_model ! Switch designating whether to use net or gross assimilation in the stomata model - ! 1 for net, 2 for gross - - integer, public :: hlm_stomatal_model ! switch for choosing between stomatal conductance models - ! 1 for Ball-Berry, 2 for Medlyn - integer, public :: hlm_maintresp_leaf_model ! switch for choosing between leaf maintenance ! respiration model. 1=Ryan (1991), 2=Atkin et al (2017) @@ -189,11 +178,6 @@ module FatesInterfaceTypesMod integer, public :: hlm_radiation_model ! Switch for radiation model ! Norman (1) and Two-stream (2) - integer, public :: hlm_electron_transport_model ! Switch for electron transport model - ! (1) for Farquhar von Caemmerer & Berry (FvCB) - ! (2) for Johnson & Berry (2021) (JB) - - integer, public :: hlm_regeneration_model ! Switch for choosing between regeneration models: ! (1) for Fates default ! (2) for the Tree Recruitment Scheme (Hanbury-Brown et al., 2022) @@ -813,6 +797,15 @@ module FatesInterfaceTypesMod real(r8) :: gpp_site ! Site level GPP, for NBP diagnosis in HLM [Site-Level, gC m-2 s-1] real(r8) :: ar_site ! Site level Autotrophic Resp, for NBP diagnosis in HLM [Site-Level, gC m-2 s-1] + ! direct carbon loss to atm pathways + real(r8) :: grazing_closs_to_atm_si ! Loss of carbon to atmosphere via grazing [Site-Level, gC m-2 s-1] + real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] + + ! summary carbon stock variables + real(r8) :: veg_c_si ! Total vegetation carbon [Site-Level, gC m-2] + real(r8) :: litter_cwd_c_si ! Total litter plus CWD carbon [Site-Level, gC m-2] + real(r8) :: seed_c_si ! Total seed carbon [Site-Level, gC m-2] + end type bc_out_type @@ -849,10 +842,23 @@ module FatesInterfaceTypesMod ! increasing, or all 1s) end type bc_pconst_type - + + public :: ZeroBCOutCarbonFluxes + contains - ! ====================================================================================== - - - end module FatesInterfaceTypesMod + ! ====================================================================================== + + subroutine ZeroBCOutCarbonFluxes(bc_out) + + ! !ARGUMENTS + type(bc_out_type), intent(inout) :: bc_out + + bc_out%grazing_closs_to_atm_si = nan ! set via site_mass%burn_flux + bc_out%fire_closs_to_atm_si = nan ! set via site_mass%herbivory_flux_out + bc_out%gpp_site = 0._r8 + bc_out%ar_site = 0._r8 + + end subroutine ZeroBCOutCarbonFluxes + +end module FatesInterfaceTypesMod diff --git a/main/FatesInventoryInitMod.F90 b/main/FatesInventoryInitMod.F90 index b5fe1acfe9..6673f4b819 100644 --- a/main/FatesInventoryInitMod.F90 +++ b/main/FatesInventoryInitMod.F90 @@ -28,7 +28,6 @@ module FatesInventoryInitMod ! FATES GLOBALS use FatesConstantsMod, only : r8 => fates_r8 use FatesConstantsMod, only : pi_const - use FatesConstantsMod, only : itrue use FatesConstantsMod, only : nearzero use FatesGlobals , only : endrun => fates_endrun use FatesGlobals , only : fates_log @@ -45,6 +44,8 @@ module FatesInventoryInitMod use EDTypesMod , only : area use FatesConstantsMod, only : leaves_on use FatesConstantsMod, only : leaves_off + use FatesConstantsMod, only : ievergreen + use FatesConstantsMod, only : ihard_season_decid use FatesConstantsMod, only : ihard_stress_decid use FatesConstantsMod, only : isemi_stress_decid use PRTGenericMod , only : num_elements @@ -970,17 +971,24 @@ subroutine set_inventory_cohort_type1(csite,bc_in,css_file_unit,npatches, & fnrt_drop_fraction = prt_params%phen_fnrt_drop_fraction(temp_cohort%pft) stem_drop_fraction = prt_params%phen_stem_drop_fraction(temp_cohort%pft) - if( prt_params%season_decid(temp_cohort%pft) == itrue .and. & - any(csite%cstatus == [phen_cstat_nevercold,phen_cstat_iscold])) then - ! Cold deciduous and season is for leaves off. Set leaf status and - ! elongation factors accordingly - temp_cohort%efleaf_coh = 0.0_r8 - temp_cohort%effnrt_coh = 1._r8 - fnrt_drop_fraction - temp_cohort%efstem_coh = 1._r8 - stem_drop_fraction - - temp_cohort%status_coh = leaves_off + phen_select: select case (prt_params%phen_leaf_habit(temp_cohort%pft)) + case (ihard_season_decid) + if ( any(csite%cstatus == [phen_cstat_nevercold,phen_cstat_iscold]) ) then + ! Cold deciduous and season is for leaves off. Set leaf status and + ! elongation factors accordingly + temp_cohort%efleaf_coh = 0.0_r8 + temp_cohort%effnrt_coh = 1._r8 - fnrt_drop_fraction + temp_cohort%efstem_coh = 1._r8 - stem_drop_fraction + temp_cohort%status_coh = leaves_off + else + ! Cold deciduous during the growing season. Assume tissues are fully flushed. + temp_cohort%efleaf_coh = 1.0_r8 + temp_cohort%effnrt_coh = 1.0_r8 + temp_cohort%efstem_coh = 1.0_r8 + temp_cohort%status_coh = leaves_on + end if - elseif ( any(prt_params%stress_decid(temp_cohort%pft) == [ihard_stress_decid,isemi_stress_decid])) then + case (ihard_stress_decid,isemi_stress_decid) ! Drought deciduous. For the default approach, elongation factor is either ! zero (full abscission) or one (fully flushed), but this can also be a ! fraction in other approaches. Here we assume that leaves are "on" (i.e. @@ -1002,14 +1010,13 @@ subroutine set_inventory_cohort_type1(csite,bc_in,css_file_unit,npatches, & ! Leaves are off (abscissing). temp_cohort%status_coh = leaves_off end if - else - ! Evergreen, or deciduous PFT during the growing season. Assume tissues are fully flushed. + case (ievergreen) + ! Evergreen. Assume tissues are fully flushed. temp_cohort%efleaf_coh = 1.0_r8 temp_cohort%effnrt_coh = 1.0_r8 temp_cohort%efstem_coh = 1.0_r8 - temp_cohort%status_coh = leaves_on - end if + end select phen_select call bagw_allom(temp_cohort%dbh,temp_cohort%pft, & temp_cohort%crowndamage, temp_cohort%efstem_coh, c_agw) diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index 9a6c9fcde1..6a30cd3f0d 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -50,8 +50,9 @@ module FatesRestartInterfaceMod use EDTypesMod, only : area use EDTypesMod, only : set_patchno use EDParamsMod, only : nlevleaf - use PRTGenericMod, only : prt_global + use PRTGenericMod, only : carbon12_element use PRTGenericMod, only : num_elements + use PRTGenericMod, only : element_pos use FatesRunningMeanMod, only : rmean_type use FatesRunningMeanMod, only : ema_lpa use FatesRadiationMemMod, only : num_swb,norman_solver,twostr_solver @@ -111,8 +112,10 @@ module FatesRestartInterfaceMod integer :: ir_landuse_vector_gt_min_si integer :: ir_area_bareground_si integer :: ir_snow_depth_si - integer :: ir_trunk_product_si integer :: ir_landuse_config_si + integer :: ir_gpp_acc_si + integer :: ir_aresp_acc_si + integer :: ir_ncohort_pa integer :: ir_canopy_layer_co integer :: ir_canopy_layer_yesterday_co @@ -243,9 +246,17 @@ module FatesRestartInterfaceMod integer :: ir_area_pft_sift integer :: ir_fmortrate_cano_siscpf integer :: ir_fmortrate_usto_siscpf + integer :: ir_nonrx_fmortrate_cano_siscpf + integer :: ir_nonrx_fmortrate_usto_siscpf + integer :: ir_rx_fmortrate_cano_siscpf + integer :: ir_rx_fmortrate_usto_siscpf integer :: ir_imortrate_siscpf integer :: ir_fmortrate_crown_siscpf integer :: ir_fmortrate_cambi_siscpf + integer :: ir_nonrx_fmortrate_crown_siscpf + integer :: ir_nonrx_fmortrate_cambi_siscpf + integer :: ir_rx_fmortrate_crown_siscpf + integer :: ir_rx_fmortrate_cambi_siscpf integer :: ir_termnindiv_cano_siscpf integer :: ir_termnindiv_usto_siscpf integer :: ir_growflx_fusion_siscpf @@ -257,6 +268,10 @@ module FatesRestartInterfaceMod integer :: ir_imortcarea_si integer :: ir_fmortcarea_cano_si integer :: ir_fmortcarea_usto_si + integer :: ir_nonrx_fmortcarea_cano_si + integer :: ir_nonrx_fmortcarea_usto_si + integer :: ir_rx_fmortcarea_cano_si + integer :: ir_rx_fmortcarea_usto_si integer :: ir_termcflux_cano_sipft integer :: ir_termcflux_usto_sipft integer :: ir_democflux_si @@ -264,9 +279,15 @@ module FatesRestartInterfaceMod integer :: ir_imortcflux_sipft integer :: ir_fmortcflux_cano_sipft integer :: ir_fmortcflux_usto_sipft + integer :: ir_nonrx_fmortcflux_cano_sipft + integer :: ir_nonrx_fmortcflux_usto_sipft + integer :: ir_rx_fmortcflux_cano_sipft + integer :: ir_rx_fmortcflux_usto_sipft integer :: ir_abg_term_flux_siscpf integer :: ir_abg_imort_flux_siscpf integer :: ir_abg_fmort_flux_siscpf + integer :: ir_abg_nonrx_fmort_flux_siscpf + integer :: ir_abg_rx_fmort_flux_siscpf integer :: ir_disturbance_rates_siluludi @@ -294,11 +315,19 @@ module FatesRestartInterfaceMod integer :: ir_termnindiv_usto_sicdpf integer :: ir_fmortrate_cano_sicdpf integer :: ir_fmortrate_usto_sicdpf + integer :: ir_nonrx_fmortrate_cano_sicdpf + integer :: ir_nonrx_fmortrate_usto_sicdpf + integer :: ir_rx_fmortrate_cano_sicdpf + integer :: ir_rx_fmortrate_usto_sicdpf integer :: ir_imortcflux_sicdsc integer :: ir_termcflux_cano_sicdsc integer :: ir_termcflux_usto_sicdsc integer :: ir_fmortcflux_cano_sicdsc integer :: ir_fmortcflux_usto_sicdsc + integer :: ir_nonrx_fmortcflux_cano_sicdsc + integer :: ir_nonrx_fmortcflux_usto_sicdsc + integer :: ir_rx_fmortcflux_cano_sicdsc + integer :: ir_rx_fmortcflux_usto_sicdsc integer :: ir_crownarea_cano_si integer :: ir_crownarea_usto_si integer :: ir_emanpp_si @@ -317,7 +346,8 @@ module FatesRestartInterfaceMod ! The number of variable dim/kind types we have defined (static) integer, parameter, public :: fates_restart_num_dimensions = 2 !(cohort,column) - integer, parameter, public :: fates_restart_num_dim_kinds = 4 !(cohort-int,cohort-r8,site-int,site-r8) + integer, parameter, public :: fates_restart_num_dim_kinds = 4 !(cohort-int,cohort-r8, + ! site-int,site-r8) ! integer constants for storing logical data integer, parameter, public :: old_cohort = 0 @@ -740,16 +770,21 @@ subroutine define_restart_vars(this, initialize_variables) long_name='average snow depth', units='m', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_snow_depth_si ) - call this%set_restart_var(vname='fates_trunk_product_site', vtype=site_r8, & - long_name='Accumulate trunk product flux at site', & - units='kgC/m2', flushval = flushzero, & - hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_trunk_product_si ) - call this%set_restart_var(vname='fates_landuse_config_site', vtype=site_int, & long_name='hlm_use_potentialveg status of run that created this restart file', & units='kgC/m2', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_landuse_config_si ) + call this%set_restart_var(vname='fates_massbal_gpp', vtype=site_r8, & + long_name='accumulated gpp over previous day cycle', & + units='kgC/m2/s', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_gpp_acc_si ) + + call this%set_restart_var(vname='fates_massbal_ar', vtype=site_r8, & + long_name='accumulated autotrophic respiration over previous day cycle', & + units='kgC/m2/s', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_aresp_acc_si ) + ! ----------------------------------------------------------------------------------- ! Variables stored within cohort vectors ! Note: Some of these are multi-dimensional variables in the patch/site dimension @@ -1375,29 +1410,69 @@ subroutine define_restart_vars(this, initialize_variables) hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_seed_out_sift ) call this%set_restart_var(vname='fates_fmortrate_canopy', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality canopy', & + long_name='fates diagnostics on total fire mortality canopy', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_siscpf) call this%set_restart_var(vname='fates_fmortrate_ustory', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality ustory', & + long_name='fates diagnostics on total fire mortality ustory', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_siscpf) + call this%set_restart_var(vname='fates_nonrx_fmortrate_canopy', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality canopy', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cano_siscpf) + + call this%set_restart_var(vname='fates_nonrx_fmortrate_ustory', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality ustory', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_usto_siscpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_canopy', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality canopy', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cano_siscpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_ustory', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality ustory', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_usto_siscpf) + call this%set_restart_var(vname='fates_imortrate', vtype=cohort_r8, & long_name='fates diagnostics on impact mortality', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortrate_siscpf) - + call this%set_restart_var(vname='fates_fmortrate_crown', vtype=cohort_r8, & - long_name='fates diagnostics on crown fire mortality', & + long_name='fates diagnostics on total crown fire mortality', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_crown_siscpf) call this%set_restart_var(vname='fates_fmortrate_cambi', vtype=cohort_r8, & - long_name='fates diagnostics on fire cambial mortality', & + long_name='fates diagnostics on total fire cambial mortality', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cambi_siscpf) + + call this%set_restart_var(vname='fates_nonrx_fmortrate_crown', vtype=cohort_r8, & + long_name='fates diagnostics on crown fire mortality for wildfire', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_crown_siscpf) + + call this%set_restart_var(vname='fates_nonrx_fmortrate_cambi', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire cambial mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cambi_siscpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_crown', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire crown fire mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_crown_siscpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_cambi', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire cambial mortality', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cambi_siscpf) call this%set_restart_var(vname='fates_termn_canopy', vtype=cohort_r8, & long_name='fates diagnostics on termin mortality canopy', & @@ -1429,12 +1504,12 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcflux_sipft) - call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & + call this%set_restart_var(vname='fates_imortcarea', vtype=site_r8, & long_name='crownarea of indivs killed due to impact mort', & units='m2/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_imortcarea_si) - call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_fmortcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic biomass of canopy fire', & units='gC/m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_cano_sipft) @@ -1444,42 +1519,72 @@ subroutine define_restart_vars(this, initialize_variables) units='gC/m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sipft) + call this%set_restart_var(vname='fates_nonrx_fmortcflux_canopy', vtype=cohort_r8, & + long_name='fates diagnostic biomass of canopy wildfire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_cano_sipft) + + call this%set_restart_var(vname='fates_nonrx_fmortcflux_ustory', vtype=cohort_r8, & + long_name='fates diagnostic biomass of understory wildfire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_usto_sipft) + + call this%set_restart_var(vname='fates_rx_fmortcflux_canopy', vtype=cohort_r8, & + long_name='fates diagnostic biomass of canopy rx fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_cano_sipft) + + call this%set_restart_var(vname='fates_rx_fmortcflux_ustory', vtype=cohort_r8, & + long_name='fates diagnostic biomass of understory rx fire', & + units='gC/m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_usto_sipft) + call this%set_restart_var(vname='fates_termcflux_canopy', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux canopy', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_cano_sipft ) - call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_termcflux_ustory', vtype=cohort_r8, & long_name='fates diagnostic term carbon flux understory', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcflux_usto_sipft ) - call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_term_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from termination mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_term_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_imort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from impact mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_imort_flux_siscpf ) - call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & + call this%set_restart_var(vname='fates_abg_fmort_flux', vtype=cohort_r8, & long_name='fates aboveground biomass loss from fire mortality', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_fmort_flux_siscpf ) - call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_abg_nonrx_fmort_flux', vtype=cohort_r8, & + long_name='fates aboveground biomass loss from wildfire mortality', & + units='', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_nonrx_fmort_flux_siscpf ) + + call this%set_restart_var(vname='fates_abg_rx_fmort_flux', vtype=cohort_r8, & + long_name='fates aboveground biomass loss from rx fire mortality', & + units='', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_abg_rx_fmort_flux_siscpf ) + + call this%set_restart_var(vname='fates_democflux', vtype=site_r8, & long_name='fates diagnostic demotion carbon flux', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_democflux_si ) - call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & + call this%set_restart_var(vname='fates_promcflux', vtype=site_r8, & long_name='fates diagnostic promotion carbon flux ', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_promcflux_si ) - call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & + call this%set_restart_var(vname='fates_fmortcarea_canopy', vtype=site_r8, & long_name='fates diagnostic crownarea of canopy fire', & units='m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_cano_si) @@ -1489,12 +1594,32 @@ subroutine define_restart_vars(this, initialize_variables) units='m2/sec', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcarea_usto_si) + call this%set_restart_var(vname='fates_nonrx_fmortcarea_canopy', vtype=site_r8, & + long_name='fates diagnostic crownarea of canopy wildfire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcarea_cano_si) + + call this%set_restart_var(vname='fates_nonrx_fmortcarea_ustory', vtype=site_r8, & + long_name='fates diagnostic crownarea of understory wildfire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcarea_usto_si) + + call this%set_restart_var(vname='fates_rx_fmortcarea_canopy', vtype=site_r8, & + long_name='fates diagnostic crownarea of canopy rx fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcarea_cano_si) + + call this%set_restart_var(vname='fates_rx_fmortcarea_ustory', vtype=site_r8, & + long_name='fates diagnostic crownarea of understory rx fire', & + units='m2/sec', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcarea_usto_si) + call this%set_restart_var(vname='fates_termcarea_canopy', vtype=site_r8, & long_name='fates diagnostic term crownarea canopy', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcarea_cano_si ) - call this%set_restart_var(vname='fates_termcarea_ustory', vtype=site_r8, & + call this%set_restart_var(vname='fates_termcarea_ustory', vtype=site_r8, & long_name='fates diagnostic term crownarea understory', & units='', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termcarea_usto_si ) @@ -1516,15 +1641,35 @@ subroutine define_restart_vars(this, initialize_variables) hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_termnindiv_usto_sicdpf) call this%set_restart_var(vname='fates_fmortrate_cano_dam', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality by damage class', & + long_name='fates diagnostics on wildfire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_cano_sicdpf) call this%set_restart_var(vname='fates_fmortrate_usto_dam', vtype=cohort_r8, & - long_name='fates diagnostics on fire mortality by damage class', & + long_name='fates diagnostics on wildfire mortality by damage class', & units='indiv/ha/year', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortrate_usto_sicdpf) + call this%set_restart_var(vname='fates_nonrx_fmortrate_cano_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_cano_sicdpf) + + call this%set_restart_var(vname='fates_nonrx_fmortrate_usto_dam', vtype=cohort_r8, & + long_name='fates diagnostics on wildfire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortrate_usto_sicdpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_cano_dam', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_cano_sicdpf) + + call this%set_restart_var(vname='fates_rx_fmortrate_usto_dam', vtype=cohort_r8, & + long_name='fates diagnostics on rx fire mortality by damage class', & + units='indiv/ha/year', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortrate_usto_sicdpf) + call this%set_restart_var(vname='fates_imortcflux_dam', vtype=cohort_r8, & long_name='biomass of indivs killed due to impact mort by damage class', & units='kgC/ha/day', flushval = flushzero, & @@ -1550,6 +1695,26 @@ subroutine define_restart_vars(this, initialize_variables) units='kgC/ha/day', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_fmortcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_nonrx_fmortcflux_cano_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to wildfire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_cano_sicdsc) + + call this%set_restart_var(vname='fates_nonrx_fmortcflux_usto_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to wildfire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_nonrx_fmortcflux_usto_sicdsc) + + call this%set_restart_var(vname='fates_rx_fmortcflux_cano_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to rx fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_cano_sicdsc) + + call this%set_restart_var(vname='fates_rx_fmortcflux_usto_dam', vtype=cohort_r8, & + long_name='biomass of indivs killed due to rx fire mort by damage class', & + units='kgC/ha/day', flushval = flushzero, & + hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_rx_fmortcflux_usto_sicdsc) + call this%set_restart_var(vname='fates_crownarea_canopy_damage', vtype=site_r8, & long_name='fates area lost from damage each year', & units='m2/ha/year', flushval = flushzero, & @@ -1565,13 +1730,13 @@ subroutine define_restart_vars(this, initialize_variables) units='kg/m2/yr', flushval = flushzero, & hlms='CLM:ALM', initialize=initialize_variables, ivar=ivar, index = ir_emanpp_si) - call this%DefineRMeanRestartVar(vname='fates_tveg24patch',vtype=cohort_r8, & - long_name='24-hour patch veg temp', & - units='K', initialize=initialize_variables,ivar=ivar, index = ir_tveg24_pa) + call this%DefineRMeanRestartVar(vname='fates_tveg24patch',vtype=cohort_r8, & + long_name='24-hour patch veg temp', & + units='K', initialize=initialize_variables,ivar=ivar, index = ir_tveg24_pa) - call this%DefineRMeanRestartVar(vname='fates_disturbance_rates',vtype=cohort_r8, & - long_name='disturbance rates by donor land-use type, receiver land-use type, and disturbance type', & - units='1/day', initialize=initialize_variables,ivar=ivar, index = ir_disturbance_rates_siluludi) + call this%DefineRMeanRestartVar(vname='fates_disturbance_rates',vtype=cohort_r8, & + long_name='disturbance rates by donor land-use type, receiver land-use type, and disturbance type', & + units='1/day', initialize=initialize_variables,ivar=ivar, index = ir_disturbance_rates_siluludi) if ( hlm_regeneration_model == TRS_regeneration ) then @@ -1593,8 +1758,7 @@ subroutine define_restart_vars(this, initialize_variables) call this%DefineRMeanRestartVar(vname='fates_sdlng_mdd',vtype=cohort_r8, & long_name='seedling moisture deficit days', & - units='mm days', initialize=initialize_variables,ivar=ivar, index = ir_sdlng_mdd_pa) - + units='mm days', initialize=initialize_variables,ivar=ivar, index = ir_sdlng_mdd_pa) end if call this%DefineRMeanRestartVar(vname='fates_tveglpapatch',vtype=cohort_r8, & @@ -2081,6 +2245,7 @@ subroutine set_restart_vectors(this,nc,nsites,sites) integer :: ft ! functional type index integer :: el ! element loop index + integer :: c_el ! element loop index for carbon12 integer :: ilyr ! soil layer index integer :: nlevsoil ! total soil layers in patch of interest integer :: k,j,i ! indices to the radiation matrix @@ -2119,7 +2284,6 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_landuse_vector_gt_min_si => this%rvars(ir_landuse_vector_gt_min_si)%int1d, & rio_area_bareground_si => this%rvars(ir_area_bareground_si)%r81d, & rio_snow_depth_si => this%rvars(ir_snow_depth_si)%r81d, & - rio_trunk_product_si => this%rvars(ir_trunk_product_si)%r81d, & rio_landuse_config_s => this%rvars(ir_landuse_config_si)%int1d, & rio_ncohort_pa => this%rvars(ir_ncohort_pa)%int1d, & rio_fcansno_pa => this%rvars(ir_fcansno_pa)%r81d, & @@ -2188,9 +2352,17 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & + rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & + rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & + rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & + rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & + rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & + rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & + rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & + rio_rx_fmortrate_cambi_siscpf => this%rvars(ir_rx_fmortrate_cambi_siscpf)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & rio_growflx_fusion_siscpf => this%rvars(ir_growflx_fusion_siscpf)%r81d, & @@ -2202,6 +2374,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & + rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & + rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & + rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & + rio_rx_fmortcarea_usto_si => this%rvars(ir_rx_fmortcarea_usto_si)%r81d, & rio_termcflux_cano_sipft => this%rvars(ir_termcflux_cano_sipft)%r81d, & rio_termcflux_usto_sipft => this%rvars(ir_termcflux_usto_sipft)%r81d, & rio_democflux_si => this%rvars(ir_democflux_si)%r81d, & @@ -2209,8 +2385,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & + rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & + rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & + rio_rx_fmortcflux_cano_sipft => this%rvars(ir_rx_fmortcflux_cano_sipft)%r81d, & + rio_rx_fmortcflux_usto_sipft => this%rvars(ir_rx_fmortcflux_usto_sipft)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & + rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & + rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_landuse_config_si => this%rvars(ir_landuse_config_si)%int1d, & @@ -2225,6 +2407,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & + rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & + rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & + rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & + rio_nonrx_fmortcflux_usto_sicdsc => this%rvars(ir_nonrx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortrate_cano_sicdpf => this%rvars(ir_rx_fmortrate_cano_sicdpf)%r81d, & + rio_rx_fmortrate_usto_sicdpf => this%rvars(ir_rx_fmortrate_usto_sicdpf)%r81d, & + rio_rx_fmortcflux_cano_sicdsc => this%rvars(ir_rx_fmortcflux_cano_sicdsc)%r81d, & + rio_rx_fmortcflux_usto_sicdsc => this%rvars(ir_rx_fmortcflux_usto_sicdsc)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d) @@ -2300,13 +2490,23 @@ subroutine set_restart_vectors(this,nc,nsites,sites) do i_pft = 1, numpft rio_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_canopy(i_scls, i_pft) rio_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_ustory(i_scls, i_pft) + rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) + rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) rio_imortrate_siscpf(io_idx_si_scpf) = sites(s)%imort_rate(i_scls, i_pft) rio_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_crown(i_scls, i_pft) rio_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%fmort_rate_cambial(i_scls, i_pft) + rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) + rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) + rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) + rio_rx_fmortrate_usto_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_ustory(i_scls, i_pft) + rio_rx_fmortrate_crown_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_crown(i_scls, i_pft) + rio_rx_fmortrate_cambi_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_rate_cambial(i_scls, i_pft) rio_growflx_fusion_siscpf(io_idx_si_scpf) = sites(s)%growthflux_fusion(i_scls, i_pft) rio_abg_term_flux_siscpf(io_idx_si_scpf) = sites(s)%term_abg_flux(i_scls, i_pft) rio_abg_imort_flux_siscpf(io_idx_si_scpf) = sites(s)%imort_abg_flux(i_scls, i_pft) rio_abg_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%fmort_abg_flux(i_scls, i_pft) + rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%nonrx_fmort_abg_flux(i_scls, i_pft) + rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) = sites(s)%rx_fmort_abg_flux(i_scls, i_pft) io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) = sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) @@ -2324,6 +2524,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) end do rio_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_canopy(i_pft) rio_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%fmort_carbonflux_ustory(i_pft) + rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) + rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) + rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_canopy(i_pft) + rio_rx_fmortcflux_usto_sipft(io_idx_si_pft) = sites(s)%rx_fmort_carbonflux_ustory(i_pft) rio_imortcflux_sipft(io_idx_si_pft) = sites(s)%imort_carbonflux(i_pft) rio_dd_status_sift(io_idx_si_pft) = sites(s)%dstatus(i_pft) rio_dleafondate_sift(io_idx_si_pft) = sites(s)%dleafondate(i_pft) @@ -2379,7 +2583,9 @@ subroutine set_restart_vectors(this,nc,nsites,sites) end do end if - + c_el = element_pos(carbon12_element) + this%rvars(ir_gpp_acc_si)%r81d(io_idx_si) = sites(s)%mass_balance(c_el)%gpp_acc + this%rvars(ir_aresp_acc_si)%r81d(io_idx_si) = sites(s)%mass_balance(c_el)%aresp_acc ! canopy spread term rio_spread_si(io_idx_si) = sites(s)%spread @@ -2710,6 +2916,14 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) + rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) + rio_nonrx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%nonrx_fmort_cflux_ustory_damage(i_cdam, i_scls) + rio_rx_fmortrate_cano_sicdpf(io_idx_si_cdpf) = sites(s)%rx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) + rio_rx_fmortrate_usto_sicdpf(io_idx_si_cdpf) = sites(s)%rx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) + rio_rx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) = sites(s)%rx_fmort_cflux_canopy_damage(i_cdam, i_scls) + rio_rx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) = sites(s)%rx_fmort_cflux_ustory_damage(i_cdam, i_scls) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -2728,6 +2942,10 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_imortcarea_si(io_idx_si) = sites(s)%imort_crownarea rio_fmortcarea_cano_si(io_idx_si) = sites(s)%fmort_crownarea_canopy rio_fmortcarea_usto_si(io_idx_si) = sites(s)%fmort_crownarea_ustory + rio_nonrx_fmortcarea_cano_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_canopy + rio_nonrx_fmortcarea_usto_si(io_idx_si) = sites(s)%nonrx_fmort_crownarea_ustory + rio_rx_fmortcarea_cano_si(io_idx_si) = sites(s)%rx_fmort_crownarea_canopy + rio_rx_fmortcarea_usto_si(io_idx_si) = sites(s)%rx_fmort_crownarea_ustory rio_cd_status_si(io_idx_si) = sites(s)%cstatus rio_nchill_days_si(io_idx_si) = sites(s)%nchilldays @@ -2744,9 +2962,6 @@ subroutine set_restart_vectors(this,nc,nsites,sites) rio_fireweather_index_si(io_idx_si) = sites(s)%fireWeather%fire_weather_index rio_snow_depth_si(io_idx_si) = sites(s)%snow_depth - ! Accumulated trunk product - rio_trunk_product_si(io_idx_si) = sites(s)%resources_management%trunk_product_site - ! land use flag rio_landuse_config_si(io_idx_si) = hlm_use_potentialveg @@ -3085,6 +3300,7 @@ subroutine get_restart_vectors(this, nc, nsites, sites) integer :: patchespersite ! number of patches per site integer :: cohortsperpatch ! number of cohorts per patch integer :: el ! loop counter for elements + integer :: c_el ! loop counter for carbon12 integer :: nlevsoil ! number of soil layers integer :: ilyr ! soil layer loop counter integer :: iscpf ! multiplex loop counter for size x pft @@ -3117,7 +3333,6 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_landuse_vector_gt_min_si => this%rvars(ir_landuse_vector_gt_min_si)%int1d, & rio_area_bareground_si => this%rvars(ir_area_bareground_si)%r81d, & rio_snow_depth_si => this%rvars(ir_snow_depth_si)%r81d, & - rio_trunk_product_si => this%rvars(ir_trunk_product_si)%r81d, & rio_landuse_config_si => this%rvars(ir_landuse_config_si)%int1d, & rio_ncohort_pa => this%rvars(ir_ncohort_pa)%int1d, & rio_fcansno_pa => this%rvars(ir_fcansno_pa)%r81d, & @@ -3186,9 +3401,17 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_seed_out_sift => this%rvars(ir_seed_out_sift)%r81d, & rio_fmortrate_cano_siscpf => this%rvars(ir_fmortrate_cano_siscpf)%r81d, & rio_fmortrate_usto_siscpf => this%rvars(ir_fmortrate_usto_siscpf)%r81d, & + rio_nonrx_fmortrate_cano_siscpf => this%rvars(ir_nonrx_fmortrate_cano_siscpf)%r81d, & + rio_nonrx_fmortrate_usto_siscpf => this%rvars(ir_nonrx_fmortrate_usto_siscpf)%r81d, & + rio_rx_fmortrate_cano_siscpf => this%rvars(ir_rx_fmortrate_cano_siscpf)%r81d, & + rio_rx_fmortrate_usto_siscpf => this%rvars(ir_rx_fmortrate_usto_siscpf)%r81d, & rio_imortrate_siscpf => this%rvars(ir_imortrate_siscpf)%r81d, & rio_fmortrate_crown_siscpf => this%rvars(ir_fmortrate_crown_siscpf)%r81d, & rio_fmortrate_cambi_siscpf => this%rvars(ir_fmortrate_cambi_siscpf)%r81d, & + rio_nonrx_fmortrate_crown_siscpf => this%rvars(ir_nonrx_fmortrate_crown_siscpf)%r81d, & + rio_nonrx_fmortrate_cambi_siscpf => this%rvars(ir_nonrx_fmortrate_cambi_siscpf)%r81d, & + rio_rx_fmortrate_crown_siscpf => this%rvars(ir_rx_fmortrate_crown_siscpf)%r81d, & + rio_rx_fmortrate_cambi_siscpf => this%rvars(ir_rx_fmortrate_cambi_siscpf)%r81d, & rio_disturbance_rates_siluludi => this%rvars(ir_disturbance_rates_siluludi)%r81d, & rio_termnindiv_cano_siscpf => this%rvars(ir_termnindiv_cano_siscpf)%r81d, & rio_termnindiv_usto_siscpf => this%rvars(ir_termnindiv_usto_siscpf)%r81d, & @@ -3204,6 +3427,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_imortcarea_si => this%rvars(ir_imortcarea_si)%r81d, & rio_fmortcarea_cano_si => this%rvars(ir_fmortcarea_cano_si)%r81d, & rio_fmortcarea_usto_si => this%rvars(ir_fmortcarea_usto_si)%r81d, & + rio_nonrx_fmortcarea_cano_si => this%rvars(ir_nonrx_fmortcarea_cano_si)%r81d, & + rio_nonrx_fmortcarea_usto_si => this%rvars(ir_nonrx_fmortcarea_usto_si)%r81d, & + rio_rx_fmortcarea_cano_si => this%rvars(ir_rx_fmortcarea_cano_si)%r81d, & + rio_rx_fmortcarea_usto_si => this%rvars(ir_rx_fmortcarea_usto_si)%r81d, & rio_imortrate_sicdpf => this%rvars(ir_imortrate_sicdpf)%r81d, & rio_termnindiv_cano_sicdpf => this%rvars(ir_termnindiv_cano_sicdpf)%r81d, & rio_termnindiv_usto_sicdpf => this%rvars(ir_termnindiv_usto_sicdpf)%r81d, & @@ -3214,15 +3441,29 @@ subroutine get_restart_vectors(this, nc, nsites, sites) rio_fmortrate_usto_sicdpf => this%rvars(ir_fmortrate_usto_sicdpf)%r81d, & rio_fmortcflux_cano_sicdsc => this%rvars(ir_fmortcflux_cano_sicdsc)%r81d, & rio_fmortcflux_usto_sicdsc => this%rvars(ir_fmortcflux_usto_sicdsc)%r81d, & + rio_nonrx_fmortrate_cano_sicdpf => this%rvars(ir_nonrx_fmortrate_cano_sicdpf)%r81d, & + rio_nonrx_fmortrate_usto_sicdpf => this%rvars(ir_nonrx_fmortrate_usto_sicdpf)%r81d, & + rio_nonrx_fmortcflux_cano_sicdsc => this%rvars(ir_nonrx_fmortcflux_cano_sicdsc)%r81d, & + rio_nonrx_fmortcflux_usto_sicdsc => this%rvars(ir_nonrx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortrate_cano_sicdpf => this%rvars(ir_rx_fmortrate_cano_sicdpf)%r81d, & + rio_rx_fmortrate_usto_sicdpf => this%rvars(ir_rx_fmortrate_usto_sicdpf)%r81d, & + rio_rx_fmortcflux_cano_sicdsc => this%rvars(ir_rx_fmortcflux_cano_sicdsc)%r81d, & + rio_rx_fmortcflux_usto_sicdsc => this%rvars(ir_rx_fmortcflux_usto_sicdsc)%r81d, & + rio_rx_fmortcflux_cano_sipft => this%rvars(ir_rx_fmortcflux_cano_sipft)%r81d, & + rio_rx_fmortcflux_usto_sipft => this%rvars(ir_rx_fmortcflux_usto_sipft)%r81d, & rio_crownarea_cano_damage_si=> this%rvars(ir_crownarea_cano_si)%r81d, & rio_crownarea_usto_damage_si=> this%rvars(ir_crownarea_usto_si)%r81d, & rio_emanpp_si => this%rvars(ir_emanpp_si)%r81d, & rio_imortcflux_sipft => this%rvars(ir_imortcflux_sipft)%r81d, & rio_fmortcflux_cano_sipft => this%rvars(ir_fmortcflux_cano_sipft)%r81d, & rio_fmortcflux_usto_sipft => this%rvars(ir_fmortcflux_usto_sipft)%r81d, & + rio_nonrx_fmortcflux_cano_sipft => this%rvars(ir_nonrx_fmortcflux_cano_sipft)%r81d, & + rio_nonrx_fmortcflux_usto_sipft => this%rvars(ir_nonrx_fmortcflux_usto_sipft)%r81d, & rio_abg_term_flux_siscpf => this%rvars(ir_abg_term_flux_siscpf)%r81d, & rio_abg_imort_flux_siscpf => this%rvars(ir_abg_imort_flux_siscpf)%r81d, & - rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d ) + rio_abg_fmort_flux_siscpf => this%rvars(ir_abg_fmort_flux_siscpf)%r81d, & + rio_abg_nonrx_fmort_flux_siscpf => this%rvars(ir_abg_nonrx_fmort_flux_siscpf)%r81d, & + rio_abg_rx_fmort_flux_siscpf => this%rvars(ir_abg_rx_fmort_flux_siscpf)%r81d ) totalcohorts = 0 @@ -3285,13 +3526,23 @@ subroutine get_restart_vectors(this, nc, nsites, sites) do i_pft = 1, numpft sites(s)%fmort_rate_canopy(i_scls, i_pft) = rio_fmortrate_cano_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_ustory(i_scls, i_pft) = rio_fmortrate_usto_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_canopy(i_scls, i_pft) = rio_nonrx_fmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_ustory(i_scls, i_pft) = rio_nonrx_fmortrate_usto_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_canopy(i_scls, i_pft) = rio_rx_fmortrate_cano_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_ustory(i_scls, i_pft) = rio_rx_fmortrate_usto_siscpf(io_idx_si_scpf) sites(s)%imort_rate(i_scls, i_pft) = rio_imortrate_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_crown(i_scls, i_pft) = rio_fmortrate_crown_siscpf(io_idx_si_scpf) sites(s)%fmort_rate_cambial(i_scls, i_pft) = rio_fmortrate_cambi_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_crown(i_scls, i_pft) = rio_nonrx_fmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_rate_cambial(i_scls, i_pft) = rio_nonrx_fmortrate_cambi_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_crown(i_scls, i_pft) = rio_rx_fmortrate_crown_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_rate_cambial(i_scls, i_pft) = rio_rx_fmortrate_cambi_siscpf(io_idx_si_scpf) sites(s)%growthflux_fusion(i_scls, i_pft) = rio_growflx_fusion_siscpf(io_idx_si_scpf) sites(s)%term_abg_flux(i_scls,i_pft) = rio_abg_term_flux_siscpf(io_idx_si_scpf) sites(s)%imort_abg_flux(i_scls,i_pft) = rio_abg_imort_flux_siscpf(io_idx_si_scpf) sites(s)%fmort_abg_flux(i_scls,i_pft) = rio_abg_fmort_flux_siscpf(io_idx_si_scpf) + sites(s)%nonrx_fmort_abg_flux(i_scls,i_pft) = rio_abg_nonrx_fmort_flux_siscpf(io_idx_si_scpf) + sites(s)%rx_fmort_abg_flux(i_scls,i_pft) = rio_abg_rx_fmort_flux_siscpf(io_idx_si_scpf) io_idx_si_scpf = io_idx_si_scpf + 1 do i_term_type = 1, n_term_mort_types sites(s)%term_nindivs_canopy(i_term_type,i_scls,i_pft) = rio_termnindiv_cano_siscpf(io_idx_si_scpf_term) @@ -3309,6 +3560,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) end do sites(s)%fmort_carbonflux_canopy(i_pft) = rio_fmortcflux_cano_sipft(io_idx_si_pft) sites(s)%fmort_carbonflux_ustory(i_pft) = rio_fmortcflux_usto_sipft(io_idx_si_pft) + sites(s)%nonrx_fmort_carbonflux_canopy(i_pft) = rio_nonrx_fmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%nonrx_fmort_carbonflux_ustory(i_pft) = rio_nonrx_fmortcflux_usto_sipft(io_idx_si_pft) + sites(s)%rx_fmort_carbonflux_canopy(i_pft) = rio_rx_fmortcflux_cano_sipft(io_idx_si_pft) + sites(s)%rx_fmort_carbonflux_ustory(i_pft) = rio_rx_fmortcflux_usto_sipft(io_idx_si_pft) sites(s)%imort_carbonflux(i_pft) = rio_imortcflux_sipft(io_idx_si_pft) sites(s)%dstatus(i_pft) = rio_dd_status_sift(io_idx_si_pft) sites(s)%dleafondate(i_pft) = rio_dleafondate_sift(io_idx_si_pft) @@ -3364,6 +3619,11 @@ subroutine get_restart_vectors(this, nc, nsites, sites) end do end if + + c_el = element_pos(carbon12_element) + sites(s)%mass_balance(c_el)%gpp_acc = this%rvars(ir_gpp_acc_si)%r81d(io_idx_si) + sites(s)%mass_balance(c_el)%aresp_acc = this%rvars(ir_aresp_acc_si)%r81d(io_idx_si) + sites(s)%spread = rio_spread_si(io_idx_si) @@ -3739,6 +3999,14 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_fmortrate_usto_sicdpf(io_idx_si_cdpf) sites(s)%fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_fmortcflux_cano_sicdsc(io_idx_si_cdsc) sites(s)%fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_fmortcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%nonrx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%nonrx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_nonrx_fmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%nonrx_fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_nonrx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%nonrx_fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_nonrx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) + sites(s)%rx_fmort_rate_canopy_damage(i_cdam, i_scls, i_pft) = rio_rx_fmortrate_cano_sicdpf(io_idx_si_cdpf) + sites(s)%rx_fmort_rate_ustory_damage(i_cdam, i_scls, i_pft) = rio_rx_fmortrate_usto_sicdpf(io_idx_si_cdpf) + sites(s)%rx_fmort_cflux_canopy_damage(i_cdam, i_scls) = rio_rx_fmortcflux_cano_sicdsc(io_idx_si_cdsc) + sites(s)%rx_fmort_cflux_ustory_damage(i_cdam, i_scls) = rio_rx_fmortcflux_usto_sicdsc(io_idx_si_cdsc) io_idx_si_cdsc = io_idx_si_cdsc + 1 io_idx_si_cdpf = io_idx_si_cdpf + 1 end do @@ -3757,6 +4025,10 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%imort_crownarea = rio_imortcarea_si(io_idx_si) sites(s)%fmort_crownarea_canopy = rio_fmortcarea_cano_si(io_idx_si) sites(s)%fmort_crownarea_ustory = rio_fmortcarea_usto_si(io_idx_si) + sites(s)%nonrx_fmort_crownarea_canopy = rio_nonrx_fmortcarea_cano_si(io_idx_si) + sites(s)%nonrx_fmort_crownarea_ustory = rio_nonrx_fmortcarea_usto_si(io_idx_si) + sites(s)%rx_fmort_crownarea_canopy = rio_rx_fmortcarea_cano_si(io_idx_si) + sites(s)%rx_fmort_crownarea_ustory = rio_rx_fmortcarea_usto_si(io_idx_si) sites(s)%demotion_carbonflux = rio_democflux_si(io_idx_si) sites(s)%promotion_carbonflux = rio_promcflux_si(io_idx_si) @@ -3776,7 +4048,6 @@ subroutine get_restart_vectors(this, nc, nsites, sites) sites(s)%fireWeather%fire_weather_index = rio_fireweather_index_si(io_idx_si) sites(s)%snow_depth = rio_snow_depth_si(io_idx_si) - sites(s)%resources_management%trunk_product_site = rio_trunk_product_si(io_idx_si) ! if needed, trigger the special procedure to initialize land use structure from a ! restart run that did not include land use. diff --git a/parameter_files/archive/api39.0.0_050825_params_default.cdl b/parameter_files/archive/api39.0.0_050825_params_default.cdl new file mode 100644 index 0000000000..55d1d0d41c --- /dev/null +++ b/parameter_files/archive/api39.0.0_050825_params_default.cdl @@ -0,0 +1,1854 @@ +netcdf fates_params_default { +dimensions: + fates_NCWD = 4 ; + fates_history_age_bins = 7 ; + fates_history_coage_bins = 2 ; + fates_history_damage_bins = 2 ; + fates_history_height_bins = 6 ; + fates_history_size_bins = 13 ; + fates_hlm_pftno = 14 ; + fates_hydr_organs = 4 ; + fates_landuseclass = 5 ; + fates_leafage_class = 1 ; + fates_litterclass = 6 ; + fates_pft = 14 ; + fates_plant_organs = 4 ; + fates_string_length = 60 ; +variables: + double fates_history_ageclass_bin_edges(fates_history_age_bins) ; + fates_history_ageclass_bin_edges:units = "yr" ; + fates_history_ageclass_bin_edges:long_name = "Lower edges for age class bins used in age-resolved patch history output" ; + double fates_history_coageclass_bin_edges(fates_history_coage_bins) ; + fates_history_coageclass_bin_edges:units = "years" ; + fates_history_coageclass_bin_edges:long_name = "Lower edges for cohort age class bins used in cohort age resolved history output" ; + double fates_history_height_bin_edges(fates_history_height_bins) ; + fates_history_height_bin_edges:units = "m" ; + fates_history_height_bin_edges:long_name = "Lower edges for height bins used in height-resolved history output" ; + double fates_history_damage_bin_edges(fates_history_damage_bins) ; + fates_history_damage_bin_edges:units = "% crown loss" ; + fates_history_damage_bin_edges:long_name = "Lower edges for damage class bins used in cohort history output" ; + double fates_history_sizeclass_bin_edges(fates_history_size_bins) ; + fates_history_sizeclass_bin_edges:units = "cm" ; + fates_history_sizeclass_bin_edges:long_name = "Lower edges for DBH size class bins used in size-resolved cohort history output" ; + double fates_alloc_organ_id(fates_plant_organs) ; + fates_alloc_organ_id:units = "unitless" ; + fates_alloc_organ_id:long_name = "This is the global index that the organ in this file is associated with, values match those in parteh/PRTGenericMod.F90" ; + double fates_hydro_htftype_node(fates_hydr_organs) ; + fates_hydro_htftype_node:units = "unitless" ; + fates_hydro_htftype_node:long_name = "Switch that defines the hydraulic transfer functions for each organ." ; + char fates_pftname(fates_pft, fates_string_length) ; + fates_pftname:units = "unitless - string" ; + fates_pftname:long_name = "Description of plant type" ; + char fates_hydro_organ_name(fates_hydr_organs, fates_string_length) ; + fates_hydro_organ_name:units = "unitless - string" ; + fates_hydro_organ_name:long_name = "Name of plant hydraulics organs (DONT CHANGE, order matches media list in FatesHydraulicsMemMod.F90)" ; + char fates_alloc_organ_name(fates_plant_organs, fates_string_length) ; + fates_alloc_organ_name:units = "unitless - string" ; + fates_alloc_organ_name:long_name = "Name of plant organs (with alloc_organ_id, must match PRTGenericMod.F90)" ; + char fates_landuseclass_name(fates_landuseclass, fates_string_length) ; + fates_landuseclass_name:units = "unitless - string" ; + fates_landuseclass_name:long_name = "Name of the land use classes, for variables associated with dimension fates_landuseclass" ; + char fates_litterclass_name(fates_litterclass, fates_string_length) ; + fates_litterclass_name:units = "unitless - string" ; + fates_litterclass_name:long_name = "Name of the litter classes, for variables associated with dimension fates_litterclass" ; + double fates_alloc_organ_priority(fates_plant_organs, fates_pft) ; + fates_alloc_organ_priority:units = "index" ; + fates_alloc_organ_priority:long_name = "Priority level for allocation, 1: replaces turnover from storage, 2: same priority as storage use/replacement, 3: ascending in order of least importance" ; + double fates_alloc_storage_cushion(fates_pft) ; + fates_alloc_storage_cushion:units = "fraction" ; + fates_alloc_storage_cushion:long_name = "maximum size of storage C pool, relative to maximum size of leaf C pool" ; + double fates_alloc_store_priority_frac(fates_pft) ; + fates_alloc_store_priority_frac:units = "unitless" ; + fates_alloc_store_priority_frac:long_name = "for high-priority organs, the fraction of their turnover demand that is gauranteed to be replaced, and if need-be by storage" ; + double fates_allom_agb1(fates_pft) ; + fates_allom_agb1:units = "variable" ; + fates_allom_agb1:long_name = "Parameter 1 for agb allometry" ; + double fates_allom_agb2(fates_pft) ; + fates_allom_agb2:units = "variable" ; + fates_allom_agb2:long_name = "Parameter 2 for agb allometry" ; + double fates_allom_agb3(fates_pft) ; + fates_allom_agb3:units = "variable" ; + fates_allom_agb3:long_name = "Parameter 3 for agb allometry" ; + double fates_allom_agb4(fates_pft) ; + fates_allom_agb4:units = "variable" ; + fates_allom_agb4:long_name = "Parameter 4 for agb allometry" ; + double fates_allom_agb_frac(fates_pft) ; + fates_allom_agb_frac:units = "fraction" ; + fates_allom_agb_frac:long_name = "Fraction of woody biomass that is above ground" ; + double fates_allom_amode(fates_pft) ; + fates_allom_amode:units = "index" ; + fates_allom_amode:long_name = "AGB allometry function index." ; + double fates_allom_blca_expnt_diff(fates_pft) ; + fates_allom_blca_expnt_diff:units = "unitless" ; + fates_allom_blca_expnt_diff:long_name = "difference between allometric DBH:bleaf and DBH:crown area exponents" ; + double fates_allom_cmode(fates_pft) ; + fates_allom_cmode:units = "index" ; + fates_allom_cmode:long_name = "coarse root biomass allometry function index." ; + double fates_allom_d2bl1(fates_pft) ; + fates_allom_d2bl1:units = "variable" ; + fates_allom_d2bl1:long_name = "Parameter 1 for d2bl allometry" ; + double fates_allom_d2bl2(fates_pft) ; + fates_allom_d2bl2:units = "variable" ; + fates_allom_d2bl2:long_name = "Parameter 2 for d2bl allometry" ; + double fates_allom_d2bl3(fates_pft) ; + fates_allom_d2bl3:units = "unitless" ; + fates_allom_d2bl3:long_name = "Parameter 3 for d2bl allometry" ; + double fates_allom_d2ca_coefficient_max(fates_pft) ; + fates_allom_d2ca_coefficient_max:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_max:long_name = "max (savanna) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2ca_coefficient_min(fates_pft) ; + fates_allom_d2ca_coefficient_min:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_min:long_name = "min (forest) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2h1(fates_pft) ; + fates_allom_d2h1:units = "variable" ; + fates_allom_d2h1:long_name = "Parameter 1 for d2h allometry (intercept, or c)" ; + double fates_allom_d2h2(fates_pft) ; + fates_allom_d2h2:units = "variable" ; + fates_allom_d2h2:long_name = "Parameter 2 for d2h allometry (slope, or m)" ; + double fates_allom_d2h3(fates_pft) ; + fates_allom_d2h3:units = "variable" ; + fates_allom_d2h3:long_name = "Parameter 3 for d2h allometry (optional)" ; + double fates_allom_dbh_maxheight(fates_pft) ; + fates_allom_dbh_maxheight:units = "cm" ; + fates_allom_dbh_maxheight:long_name = "the diameter (if any) corresponding to maximum height, diameters may increase beyond this" ; + double fates_allom_dmode(fates_pft) ; + fates_allom_dmode:units = "index" ; + fates_allom_dmode:long_name = "crown depth allometry function index" ; + double fates_allom_fmode(fates_pft) ; + fates_allom_fmode:units = "index" ; + fates_allom_fmode:long_name = "fine root biomass allometry function index." ; + double fates_allom_fnrt_prof_a(fates_pft) ; + fates_allom_fnrt_prof_a:units = "unitless" ; + fates_allom_fnrt_prof_a:long_name = "Fine root profile function, parameter a" ; + double fates_allom_fnrt_prof_b(fates_pft) ; + fates_allom_fnrt_prof_b:units = "unitless" ; + fates_allom_fnrt_prof_b:long_name = "Fine root profile function, parameter b" ; + double fates_allom_fnrt_prof_mode(fates_pft) ; + fates_allom_fnrt_prof_mode:units = "index" ; + fates_allom_fnrt_prof_mode:long_name = "Index to select fine root profile function: 1) Jackson Beta, 2) 1-param exponential 3) 2-param exponential" ; + double fates_allom_frbstor_repro(fates_pft) ; + fates_allom_frbstor_repro:units = "fraction" ; + fates_allom_frbstor_repro:long_name = "fraction of bstore goes to reproduction after plant dies" ; + double fates_allom_h2cd1(fates_pft) ; + fates_allom_h2cd1:units = "variable" ; + fates_allom_h2cd1:long_name = "Parameter 1 for h2cd allometry (exp(log-intercept) or scaling). If allom_dmode=1; this is the same as former crown_depth_frac parameter" ; + double fates_allom_h2cd2(fates_pft) ; + fates_allom_h2cd2:units = "variable" ; + fates_allom_h2cd2:long_name = "Parameter 2 for h2cd allometry (log-slope or exponent). If allom_dmode=1; this is not needed (as exponent is assumed 1)" ; + double fates_allom_hmode(fates_pft) ; + fates_allom_hmode:units = "index" ; + fates_allom_hmode:long_name = "height allometry function index." ; + double fates_allom_l2fr(fates_pft) ; + fates_allom_l2fr:units = "gC/gC" ; + fates_allom_l2fr:long_name = "Allocation parameter: fine root C per leaf C" ; + double fates_allom_la_per_sa_int(fates_pft) ; + fates_allom_la_per_sa_int:units = "m2/cm2" ; + fates_allom_la_per_sa_int:long_name = "Leaf area per sapwood area, intercept" ; + double fates_allom_la_per_sa_slp(fates_pft) ; + fates_allom_la_per_sa_slp:units = "m2/cm2/m" ; + fates_allom_la_per_sa_slp:long_name = "Leaf area per sapwood area rate of change with height, slope (optional)" ; + double fates_allom_lmode(fates_pft) ; + fates_allom_lmode:units = "index" ; + fates_allom_lmode:long_name = "leaf biomass allometry function index." ; + double fates_allom_sai_scaler(fates_pft) ; + fates_allom_sai_scaler:units = "m2/m2" ; + fates_allom_sai_scaler:long_name = "allometric ratio of SAI per LAI" ; + double fates_allom_smode(fates_pft) ; + fates_allom_smode:units = "index" ; + fates_allom_smode:long_name = "sapwood allometry function index." ; + double fates_allom_stmode(fates_pft) ; + fates_allom_stmode:units = "index" ; + fates_allom_stmode:long_name = "storage allometry function index: 1) Storage proportional to leaf biomass (with trimming), 2) Storage proportional to maximum leaf biomass (not trimmed)" ; + double fates_allom_zroot_k(fates_pft) ; + fates_allom_zroot_k:units = "unitless" ; + fates_allom_zroot_k:long_name = "scale coefficient of logistic rooting depth model" ; + double fates_allom_zroot_max_dbh(fates_pft) ; + fates_allom_zroot_max_dbh:units = "cm" ; + fates_allom_zroot_max_dbh:long_name = "dbh at which a plant reaches the maximum value for its maximum rooting depth" ; + double fates_allom_zroot_max_z(fates_pft) ; + fates_allom_zroot_max_z:units = "m" ; + fates_allom_zroot_max_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_max_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_allom_zroot_min_dbh(fates_pft) ; + fates_allom_zroot_min_dbh:units = "cm" ; + fates_allom_zroot_min_dbh:long_name = "dbh at which the maximum rooting depth for a recruit is defined" ; + double fates_allom_zroot_min_z(fates_pft) ; + fates_allom_zroot_min_z:units = "m" ; + fates_allom_zroot_min_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_min_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_c2b(fates_pft) ; + fates_c2b:units = "ratio" ; + fates_c2b:long_name = "Carbon to biomass multiplier of bulk structural tissues" ; + double fates_cnp_eca_alpha_ptase(fates_pft) ; + fates_cnp_eca_alpha_ptase:units = "g/m3" ; + fates_cnp_eca_alpha_ptase:long_name = "(INACTIVE, KEEP AT 0) fraction of P from ptase activity sent directly to plant (ECA)" ; + double fates_cnp_eca_decompmicc(fates_pft) ; + fates_cnp_eca_decompmicc:units = "gC/m3" ; + fates_cnp_eca_decompmicc:long_name = "maximum soil microbial decomposer biomass found over depth (will be applied at a reference depth w/ exponential attenuation) (ECA)" ; + double fates_cnp_eca_km_nh4(fates_pft) ; + fates_cnp_eca_km_nh4:units = "gN/m3" ; + fates_cnp_eca_km_nh4:long_name = "half-saturation constant for plant nh4 uptake (ECA)" ; + double fates_cnp_eca_km_no3(fates_pft) ; + fates_cnp_eca_km_no3:units = "gN/m3" ; + fates_cnp_eca_km_no3:long_name = "half-saturation constant for plant no3 uptake (ECA)" ; + double fates_cnp_eca_km_p(fates_pft) ; + fates_cnp_eca_km_p:units = "gP/m3" ; + fates_cnp_eca_km_p:long_name = "half-saturation constant for plant p uptake (ECA)" ; + double fates_cnp_eca_km_ptase(fates_pft) ; + fates_cnp_eca_km_ptase:units = "gP/m3" ; + fates_cnp_eca_km_ptase:long_name = "half-saturation constant for biochemical P (ECA)" ; + double fates_cnp_eca_lambda_ptase(fates_pft) ; + fates_cnp_eca_lambda_ptase:units = "g/m3" ; + fates_cnp_eca_lambda_ptase:long_name = "(INACTIVE, KEEP AT 0) critical value for biochemical production (ECA)" ; + double fates_cnp_eca_vmax_ptase(fates_pft) ; + fates_cnp_eca_vmax_ptase:units = "gP/m2/s" ; + fates_cnp_eca_vmax_ptase:long_name = "maximum production rate for biochemical P (per m2) (ECA)" ; + double fates_cnp_nfix1(fates_pft) ; + fates_cnp_nfix1:units = "fraction" ; + fates_cnp_nfix1:long_name = "fractional surcharge added to maintenance respiration that drives symbiotic fixation" ; + double fates_cnp_nitr_store_ratio(fates_pft) ; + fates_cnp_nitr_store_ratio:units = "(gN/gN)" ; + fates_cnp_nitr_store_ratio:long_name = "storeable (labile) N, as a ratio compared to the N bound in cell structures of other organs (see code)" ; + double fates_cnp_phos_store_ratio(fates_pft) ; + fates_cnp_phos_store_ratio:units = "(gP/gP)" ; + fates_cnp_phos_store_ratio:long_name = "storeable (labile) P, as a ratio compared to the P bound in cell structures of other organs (see code)" ; + double fates_cnp_pid_kd(fates_pft) ; + fates_cnp_pid_kd:units = "unknown" ; + fates_cnp_pid_kd:long_name = "derivative constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_ki(fates_pft) ; + fates_cnp_pid_ki:units = "unknown" ; + fates_cnp_pid_ki:long_name = "integral constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_kp(fates_pft) ; + fates_cnp_pid_kp:units = "unknown" ; + fates_cnp_pid_kp:long_name = "proportional constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_prescribed_nuptake(fates_pft) ; + fates_cnp_prescribed_nuptake:units = "fraction" ; + fates_cnp_prescribed_nuptake:long_name = "Prescribed N uptake flux. 0=fully coupled simulation >0=prescribed (experimental)" ; + double fates_cnp_prescribed_puptake(fates_pft) ; + fates_cnp_prescribed_puptake:units = "fraction" ; + fates_cnp_prescribed_puptake:long_name = "Prescribed P uptake flux. 0=fully coupled simulation, >0=prescribed (experimental)" ; + double fates_cnp_store_ovrflw_frac(fates_pft) ; + fates_cnp_store_ovrflw_frac:units = "fraction" ; + fates_cnp_store_ovrflw_frac:long_name = "size of overflow storage (for excess C,N or P) as a fraction of storage target" ; + double fates_cnp_turnover_nitr_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_nitr_retrans:units = "fraction" ; + fates_cnp_turnover_nitr_retrans:long_name = "retranslocation (reabsorbtion) fraction of nitrogen in turnover of scenescing tissues" ; + double fates_cnp_turnover_phos_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_phos_retrans:units = "fraction" ; + fates_cnp_turnover_phos_retrans:long_name = "retranslocation (reabsorbtion) fraction of phosphorus in turnover of scenescing tissues" ; + double fates_cnp_vmax_nh4(fates_pft) ; + fates_cnp_vmax_nh4:units = "gN/gC/s" ; + fates_cnp_vmax_nh4:long_name = "maximum (potential) uptake rate of NH4 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_nh4 for usage)" ; + double fates_cnp_vmax_no3(fates_pft) ; + fates_cnp_vmax_no3:units = "gN/gC/s" ; + fates_cnp_vmax_no3:long_name = "maximum (potential) uptake rate of NO3 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_no3 for usage)" ; + double fates_cnp_vmax_p(fates_pft) ; + fates_cnp_vmax_p:units = "gP/gC/s" ; + fates_cnp_vmax_p:long_name = "maximum production rate for phosphorus (ECA and RD)" ; + double fates_damage_frac(fates_pft) ; + fates_damage_frac:units = "fraction" ; + fates_damage_frac:long_name = "fraction of cohort damaged in each damage event (event frequency specified in the is_it_damage_time subroutine)" ; + double fates_damage_mort_p1(fates_pft) ; + fates_damage_mort_p1:units = "fraction" ; + fates_damage_mort_p1:long_name = "inflection point of damage mortality function, a value of 0.8 means 50% mortality with 80% loss of crown, turn off with a large number" ; + double fates_damage_mort_p2(fates_pft) ; + fates_damage_mort_p2:units = "unitless" ; + fates_damage_mort_p2:long_name = "rate of mortality increase with damage" ; + double fates_damage_recovery_scalar(fates_pft) ; + fates_damage_recovery_scalar:units = "unitless" ; + fates_damage_recovery_scalar:long_name = "fraction of the cohort that recovers from damage" ; + double fates_dev_arbitrary_pft(fates_pft) ; + fates_dev_arbitrary_pft:units = "unknown" ; + fates_dev_arbitrary_pft:long_name = "Unassociated pft dimensioned free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_alpha_SH(fates_pft) ; + fates_fire_alpha_SH:units = "m / (kw/m)**(2/3)" ; + fates_fire_alpha_SH:long_name = "spitfire parameter, alpha scorch height, Equation 16 Thonicke et al 2010" ; + double fates_fire_bark_scaler(fates_pft) ; + fates_fire_bark_scaler:units = "fraction" ; + fates_fire_bark_scaler:long_name = "the thickness of a cohorts bark as a fraction of its dbh" ; + double fates_fire_crown_kill(fates_pft) ; + fates_fire_crown_kill:units = "NA" ; + fates_fire_crown_kill:long_name = "fire parameter, see equation 22 in Thonicke et al 2010" ; + double fates_frag_fnrt_fcel(fates_pft) ; + fates_frag_fnrt_fcel:units = "fraction" ; + fates_frag_fnrt_fcel:long_name = "Fine root litter cellulose fraction" ; + double fates_frag_fnrt_flab(fates_pft) ; + fates_frag_fnrt_flab:units = "fraction" ; + fates_frag_fnrt_flab:long_name = "Fine root litter labile fraction" ; + double fates_frag_fnrt_flig(fates_pft) ; + fates_frag_fnrt_flig:units = "fraction" ; + fates_frag_fnrt_flig:long_name = "Fine root litter lignin fraction" ; + double fates_frag_leaf_fcel(fates_pft) ; + fates_frag_leaf_fcel:units = "fraction" ; + fates_frag_leaf_fcel:long_name = "Leaf litter cellulose fraction" ; + double fates_frag_leaf_flab(fates_pft) ; + fates_frag_leaf_flab:units = "fraction" ; + fates_frag_leaf_flab:long_name = "Leaf litter labile fraction" ; + double fates_frag_leaf_flig(fates_pft) ; + fates_frag_leaf_flig:units = "fraction" ; + fates_frag_leaf_flig:long_name = "Leaf litter lignin fraction" ; + double fates_frag_seed_decay_rate(fates_pft) ; + fates_frag_seed_decay_rate:units = "yr-1" ; + fates_frag_seed_decay_rate:long_name = "fraction of seeds that decay per year" ; + double fates_grperc(fates_pft) ; + fates_grperc:units = "unitless" ; + fates_grperc:long_name = "Growth respiration factor" ; + double fates_hydro_avuln_gs(fates_pft) ; + fates_hydro_avuln_gs:units = "unitless" ; + fates_hydro_avuln_gs:long_name = "shape parameter for stomatal control of water vapor exiting leaf" ; + double fates_hydro_avuln_node(fates_hydr_organs, fates_pft) ; + fates_hydro_avuln_node:units = "unitless" ; + fates_hydro_avuln_node:long_name = "xylem vulnerability curve shape parameter" ; + double fates_hydro_epsil_node(fates_hydr_organs, fates_pft) ; + fates_hydro_epsil_node:units = "MPa" ; + fates_hydro_epsil_node:long_name = "bulk elastic modulus" ; + double fates_hydro_fcap_node(fates_hydr_organs, fates_pft) ; + fates_hydro_fcap_node:units = "unitless" ; + fates_hydro_fcap_node:long_name = "fraction of non-residual water that is capillary in source" ; + double fates_hydro_k_lwp(fates_pft) ; + fates_hydro_k_lwp:units = "unitless" ; + fates_hydro_k_lwp:long_name = "inner leaf humidity scaling coefficient" ; + double fates_hydro_kmax_node(fates_hydr_organs, fates_pft) ; + fates_hydro_kmax_node:units = "kg/MPa/m/s" ; + fates_hydro_kmax_node:long_name = "maximum xylem conductivity per unit conducting xylem area" ; + double fates_hydro_p50_gs(fates_pft) ; + fates_hydro_p50_gs:units = "MPa" ; + fates_hydro_p50_gs:long_name = "water potential at 50% loss of stomatal conductance" ; + double fates_hydro_p50_node(fates_hydr_organs, fates_pft) ; + fates_hydro_p50_node:units = "MPa" ; + fates_hydro_p50_node:long_name = "xylem water potential at 50% loss of conductivity" ; + double fates_hydro_p_taper(fates_pft) ; + fates_hydro_p_taper:units = "unitless" ; + fates_hydro_p_taper:long_name = "xylem taper exponent" ; + double fates_hydro_pinot_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pinot_node:units = "MPa" ; + fates_hydro_pinot_node:long_name = "osmotic potential at full turgor" ; + double fates_hydro_pitlp_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pitlp_node:units = "MPa" ; + fates_hydro_pitlp_node:long_name = "turgor loss point" ; + double fates_hydro_resid_node(fates_hydr_organs, fates_pft) ; + fates_hydro_resid_node:units = "cm3/cm3" ; + fates_hydro_resid_node:long_name = "residual water conent" ; + double fates_hydro_rfrac_stem(fates_pft) ; + fates_hydro_rfrac_stem:units = "fraction" ; + fates_hydro_rfrac_stem:long_name = "fraction of total tree resistance from troot to canopy" ; + double fates_hydro_rs2(fates_pft) ; + fates_hydro_rs2:units = "m" ; + fates_hydro_rs2:long_name = "absorbing root radius" ; + double fates_hydro_srl(fates_pft) ; + fates_hydro_srl:units = "m g-1" ; + fates_hydro_srl:long_name = "specific root length" ; + double fates_hydro_thetas_node(fates_hydr_organs, fates_pft) ; + fates_hydro_thetas_node:units = "cm3/cm3" ; + fates_hydro_thetas_node:long_name = "saturated water content" ; + double fates_hydro_vg_alpha_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_alpha_node:units = "MPa-1" ; + fates_hydro_vg_alpha_node:long_name = "(used if hydr_htftype_node = 2), capillary length parameter in van Genuchten model" ; + double fates_hydro_vg_m_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_m_node:units = "unitless" ; + fates_hydro_vg_m_node:long_name = "(used if hydr_htftype_node = 2),m in van Genuchten 1980 model, 2nd pore size distribution parameter" ; + double fates_hydro_vg_n_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_n_node:units = "unitless" ; + fates_hydro_vg_n_node:long_name = "(used if hydr_htftype_node = 2),n in van Genuchten 1980 model, pore size distribution parameter" ; + double fates_landuse_grazing_palatability(fates_pft) ; + fates_landuse_grazing_palatability:units = "unitless 0-1" ; + fates_landuse_grazing_palatability:long_name = "Relative intensity of leaf grazing/browsing per PFT" ; + double fates_landuse_harvest_pprod10(fates_pft) ; + fates_landuse_harvest_pprod10:units = "fraction" ; + fates_landuse_harvest_pprod10:long_name = "fraction of harvest wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_landuse_luc_frac_burned(fates_pft) ; + fates_landuse_luc_frac_burned:units = "fraction" ; + fates_landuse_luc_frac_burned:long_name = "fraction of land use change-generated and not-exported material that is burned (the remainder goes to litter)" ; + double fates_landuse_luc_frac_exported(fates_pft) ; + fates_landuse_luc_frac_exported:units = "fraction" ; + fates_landuse_luc_frac_exported:long_name = "fraction of land use change-generated wood material that is exported to wood product (the remainder is either burned or goes to litter)" ; + double fates_landuse_luc_pprod10(fates_pft) ; + fates_landuse_luc_pprod10:units = "fraction" ; + fates_landuse_luc_pprod10:long_name = "fraction of land use change wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_leaf_agross_btran_model(fates_pft) ; + fates_leaf_agross_btran_model:units = "index" ; + fates_leaf_agross_btran_model:long_name = "model switch for how gross assimilation affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_c3psn(fates_pft) ; + fates_leaf_c3psn:units = "flag" ; + fates_leaf_c3psn:long_name = "Photosynthetic pathway (1=c3, 0=c4)" ; + double fates_leaf_fnps(fates_pft) ; + fates_leaf_fnps:units = "fraction" ; + fates_leaf_fnps:long_name = "fraction of light absorbed by non-photosynthetic pigments" ; + double fates_leaf_jmaxha(fates_pft) ; + fates_leaf_jmaxha:units = "J/mol" ; + fates_leaf_jmaxha:long_name = "activation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxhd(fates_pft) ; + fates_leaf_jmaxhd:units = "J/mol" ; + fates_leaf_jmaxhd:long_name = "deactivation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxse(fates_pft) ; + fates_leaf_jmaxse:units = "J/mol/K" ; + fates_leaf_jmaxse:long_name = "entropy term for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_slamax(fates_pft) ; + fates_leaf_slamax:units = "m^2/gC" ; + fates_leaf_slamax:long_name = "Maximum Specific Leaf Area (SLA), even if under a dense canopy" ; + double fates_leaf_slatop(fates_pft) ; + fates_leaf_slatop:units = "m^2/gC" ; + fates_leaf_slatop:long_name = "Specific Leaf Area (SLA) at top of canopy, projected area basis" ; + double fates_leaf_stomatal_btran_model(fates_pft) ; + fates_leaf_stomatal_btran_model:units = "index" ; + fates_leaf_stomatal_btran_model:long_name = "model switch for how btran affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_stomatal_intercept(fates_pft) ; + fates_leaf_stomatal_intercept:units = "umol H2O/m**2/s" ; + fates_leaf_stomatal_intercept:long_name = "Minimum unstressed stomatal conductance for Ball-Berry model and Medlyn model" ; + double fates_leaf_stomatal_slope_ballberry(fates_pft) ; + fates_leaf_stomatal_slope_ballberry:units = "unitless" ; + fates_leaf_stomatal_slope_ballberry:long_name = "stomatal slope parameter, as per Ball-Berry" ; + double fates_leaf_stomatal_slope_medlyn(fates_pft) ; + fates_leaf_stomatal_slope_medlyn:units = "KPa**0.5" ; + fates_leaf_stomatal_slope_medlyn:long_name = "stomatal slope parameter, as per Medlyn" ; + double fates_leaf_vcmax25top(fates_leafage_class, fates_pft) ; + fates_leaf_vcmax25top:units = "umol CO2/m^2/s" ; + fates_leaf_vcmax25top:long_name = "maximum carboxylation rate of Rub. at 25C, canopy top" ; + double fates_leaf_vcmaxha(fates_pft) ; + fates_leaf_vcmaxha:units = "J/mol" ; + fates_leaf_vcmaxha:long_name = "activation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxhd(fates_pft) ; + fates_leaf_vcmaxhd:units = "J/mol" ; + fates_leaf_vcmaxhd:long_name = "deactivation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxse(fates_pft) ; + fates_leaf_vcmaxse:units = "J/mol/K" ; + fates_leaf_vcmaxse:long_name = "entropy term for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leafn_vert_scaler_coeff1(fates_pft) ; + fates_leafn_vert_scaler_coeff1:units = "unitless" ; + fates_leafn_vert_scaler_coeff1:long_name = "Coefficient one for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_leafn_vert_scaler_coeff2(fates_pft) ; + fates_leafn_vert_scaler_coeff2:units = "unitless" ; + fates_leafn_vert_scaler_coeff2:long_name = "Coefficient two for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_maintresp_leaf_atkin2017_baserate(fates_pft) ; + fates_maintresp_leaf_atkin2017_baserate:units = "umol CO2/m^2/s" ; + fates_maintresp_leaf_atkin2017_baserate:long_name = "Leaf maintenance respiration base rate parameter (r0) per Atkin et al 2017" ; + double fates_maintresp_leaf_ryan1991_baserate(fates_pft) ; + fates_maintresp_leaf_ryan1991_baserate:units = "gC/gN/s" ; + fates_maintresp_leaf_ryan1991_baserate:long_name = "Leaf maintenance respiration base rate per Ryan et al 1991" ; + double fates_maintresp_leaf_vert_scaler_coeff1(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff1:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff1:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff1." ; + double fates_maintresp_leaf_vert_scaler_coeff2(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff2:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff2:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff2." ; + double fates_maintresp_reduction_curvature(fates_pft) ; + fates_maintresp_reduction_curvature:units = "unitless (0-1)" ; + fates_maintresp_reduction_curvature:long_name = "curvature of MR reduction as f(carbon storage), 1=linear, 0=very curved" ; + double fates_maintresp_reduction_intercept(fates_pft) ; + fates_maintresp_reduction_intercept:units = "unitless (0-1)" ; + fates_maintresp_reduction_intercept:long_name = "intercept of MR reduction as f(carbon storage), 0=no throttling, 1=max throttling" ; + double fates_maintresp_reduction_upthresh(fates_pft) ; + fates_maintresp_reduction_upthresh:units = "unitless (0-1)" ; + fates_maintresp_reduction_upthresh:long_name = "upper threshold for storage biomass (relative to leaf biomass) above which MR is not reduced" ; + double fates_mort_bmort(fates_pft) ; + fates_mort_bmort:units = "1/yr" ; + fates_mort_bmort:long_name = "background mortality rate" ; + 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_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. " ; + double fates_mort_ip_size_senescence(fates_pft) ; + fates_mort_ip_size_senescence:units = "dbh cm" ; + fates_mort_ip_size_senescence:long_name = "Mortality dbh senescence inflection point. If _ this mortality term is off. Setting this value turns on size dependent mortality" ; + double fates_mort_prescribed_canopy(fates_pft) ; + fates_mort_prescribed_canopy:units = "1/yr" ; + fates_mort_prescribed_canopy:long_name = "mortality rate of canopy trees for prescribed physiology mode" ; + double fates_mort_prescribed_understory(fates_pft) ; + fates_mort_prescribed_understory:units = "1/yr" ; + fates_mort_prescribed_understory:long_name = "mortality rate of understory trees for prescribed physiology mode" ; + double fates_mort_r_age_senescence(fates_pft) ; + fates_mort_r_age_senescence:units = "mortality rate year^-1" ; + fates_mort_r_age_senescence:long_name = "Mortality age senescence rate of change. Sensible range is around 0.03-0.06. Larger values givesteeper mortality curves." ; + double fates_mort_r_size_senescence(fates_pft) ; + fates_mort_r_size_senescence:units = "mortality rate dbh^-1" ; + fates_mort_r_size_senescence:long_name = "Mortality dbh senescence rate of change. Sensible range is around 0.03-0.06. Larger values give steeper mortality curves." ; + double fates_mort_scalar_coldstress(fates_pft) ; + fates_mort_scalar_coldstress:units = "1/yr" ; + fates_mort_scalar_coldstress:long_name = "maximum mortality rate from cold stress" ; + double fates_mort_scalar_cstarvation(fates_pft) ; + fates_mort_scalar_cstarvation:units = "1/yr" ; + fates_mort_scalar_cstarvation:long_name = "maximum mortality rate from carbon starvation" ; + double fates_mort_scalar_hydrfailure(fates_pft) ; + fates_mort_scalar_hydrfailure:units = "1/yr" ; + fates_mort_scalar_hydrfailure:long_name = "maximum mortality rate from hydraulic failure" ; + double fates_mort_upthresh_cstarvation(fates_pft) ; + fates_mort_upthresh_cstarvation:units = "unitless" ; + fates_mort_upthresh_cstarvation:long_name = "threshold for storage biomass (relative to target leaf biomass) above which carbon starvation is zero" ; + double fates_nonhydro_smpsc(fates_pft) ; + fates_nonhydro_smpsc:units = "mm" ; + fates_nonhydro_smpsc:long_name = "Soil water potential at full stomatal closure" ; + double fates_nonhydro_smpso(fates_pft) ; + fates_nonhydro_smpso:units = "mm" ; + fates_nonhydro_smpso:long_name = "Soil water potential at full stomatal opening" ; + double fates_phen_cold_size_threshold(fates_pft) ; + fates_phen_cold_size_threshold:units = "cm" ; + fates_phen_cold_size_threshold:long_name = "the dbh size above which will lead to phenology-related stem and leaf drop" ; + double fates_phen_drought_threshold(fates_pft) ; + fates_phen_drought_threshold:units = "m3/m3 or mm" ; + fates_phen_drought_threshold:long_name = "threshold for drought phenology (or lower threshold for semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_evergreen(fates_pft) ; + fates_phen_evergreen:units = "logical flag" ; + fates_phen_evergreen:long_name = "Binary flag for evergreen leaf habit" ; + double fates_phen_flush_fraction(fates_pft) ; + fates_phen_flush_fraction:units = "fraction" ; + fates_phen_flush_fraction:long_name = "Upon bud-burst, the maximum fraction of storage carbon used for flushing leaves" ; + double fates_phen_fnrt_drop_fraction(fates_pft) ; + fates_phen_fnrt_drop_fraction:units = "fraction" ; + fates_phen_fnrt_drop_fraction:long_name = "fraction of fine roots to drop during drought/cold" ; + double fates_phen_mindaysoff(fates_pft) ; + fates_phen_mindaysoff:units = "days" ; + fates_phen_mindaysoff:long_name = "day threshold compared against days since leaves abscised (shed)" ; + double fates_phen_moist_threshold(fates_pft) ; + fates_phen_moist_threshold:units = "m3/m3 or mm" ; + fates_phen_moist_threshold:long_name = "upper threshold for drought phenology (only for drought semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_season_decid(fates_pft) ; + fates_phen_season_decid:units = "logical flag" ; + fates_phen_season_decid:long_name = "Binary flag for seasonal-deciduous leaf habit" ; + double fates_phen_stem_drop_fraction(fates_pft) ; + fates_phen_stem_drop_fraction:units = "fraction" ; + fates_phen_stem_drop_fraction:long_name = "fraction of stems to drop for non-woody species during drought/cold" ; + double fates_phen_stress_decid(fates_pft) ; + fates_phen_stress_decid:units = "logical flag" ; + fates_phen_stress_decid:long_name = "Flag for stress/drought-deciduous leaf habit. 0 - not stress deciduous; 1 - default drought deciduous (two target states only, fully flushed or fully abscised); 2 - semi-deciduous" ; + double fates_prescribed_npp_canopy(fates_pft) ; + fates_prescribed_npp_canopy:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_canopy:long_name = "NPP per unit crown area of canopy trees for prescribed physiology mode" ; + double fates_prescribed_npp_understory(fates_pft) ; + fates_prescribed_npp_understory:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_understory:long_name = "NPP per unit crown area of understory trees for prescribed physiology mode" ; + double fates_rad_leaf_clumping_index(fates_pft) ; + fates_rad_leaf_clumping_index:units = "fraction (0-1)" ; + fates_rad_leaf_clumping_index:long_name = "factor describing how much self-occlusion of leaf scattering elements decreases light interception" ; + double fates_rad_leaf_rhonir(fates_pft) ; + fates_rad_leaf_rhonir:units = "fraction" ; + fates_rad_leaf_rhonir:long_name = "Leaf reflectance: near-IR" ; + double fates_rad_leaf_rhovis(fates_pft) ; + fates_rad_leaf_rhovis:units = "fraction" ; + fates_rad_leaf_rhovis:long_name = "Leaf reflectance: visible" ; + double fates_rad_leaf_taunir(fates_pft) ; + fates_rad_leaf_taunir:units = "fraction" ; + fates_rad_leaf_taunir:long_name = "Leaf transmittance: near-IR" ; + double fates_rad_leaf_tauvis(fates_pft) ; + fates_rad_leaf_tauvis:units = "fraction" ; + fates_rad_leaf_tauvis:long_name = "Leaf transmittance: visible" ; + double fates_rad_leaf_xl(fates_pft) ; + fates_rad_leaf_xl:units = "unitless" ; + fates_rad_leaf_xl:long_name = "Leaf/stem orientation index" ; + double fates_rad_stem_rhonir(fates_pft) ; + fates_rad_stem_rhonir:units = "fraction" ; + fates_rad_stem_rhonir:long_name = "Stem reflectance: near-IR" ; + double fates_rad_stem_rhovis(fates_pft) ; + fates_rad_stem_rhovis:units = "fraction" ; + fates_rad_stem_rhovis:long_name = "Stem reflectance: visible" ; + double fates_rad_stem_taunir(fates_pft) ; + fates_rad_stem_taunir:units = "fraction" ; + fates_rad_stem_taunir:long_name = "Stem transmittance: near-IR" ; + double fates_rad_stem_tauvis(fates_pft) ; + fates_rad_stem_tauvis:units = "fraction" ; + fates_rad_stem_tauvis:long_name = "Stem transmittance: visible" ; + double fates_recruit_height_min(fates_pft) ; + fates_recruit_height_min:units = "m" ; + fates_recruit_height_min:long_name = "the minimum height (ie starting height) of a newly recruited plant" ; + double fates_recruit_init_density(fates_pft) ; + fates_recruit_init_density:units = "stems/m2" ; + fates_recruit_init_density:long_name = "initial seedling density for a cold-start near-bare-ground simulation. If negative sets initial tree dbh - only to be used in nocomp mode" ; + double fates_recruit_prescribed_rate(fates_pft) ; + fates_recruit_prescribed_rate:units = "n/yr" ; + fates_recruit_prescribed_rate:long_name = "recruitment rate for prescribed physiology mode" ; + double fates_recruit_seed_alloc(fates_pft) ; + fates_recruit_seed_alloc:units = "fraction" ; + fates_recruit_seed_alloc:long_name = "fraction of available carbon balance allocated to seeds" ; + double fates_recruit_seed_alloc_mature(fates_pft) ; + fates_recruit_seed_alloc_mature:units = "fraction" ; + fates_recruit_seed_alloc_mature:long_name = "fraction of available carbon balance allocated to seeds in mature plants (adds to fates_seed_alloc)" ; + double fates_recruit_seed_dbh_repro_threshold(fates_pft) ; + fates_recruit_seed_dbh_repro_threshold:units = "cm" ; + fates_recruit_seed_dbh_repro_threshold:long_name = "the diameter where the plant will increase allocation to the seed pool by fraction: fates_recruit_seed_alloc_mature" ; + double fates_recruit_seed_germination_rate(fates_pft) ; + fates_recruit_seed_germination_rate:units = "yr-1" ; + fates_recruit_seed_germination_rate:long_name = "fraction of seeds that germinate per year" ; + double fates_recruit_seed_supplement(fates_pft) ; + fates_recruit_seed_supplement:units = "KgC/m2/yr" ; + fates_recruit_seed_supplement:long_name = "Supplemental external seed rain source term (non-mass conserving)" ; + double fates_seed_dispersal_fraction(fates_pft) ; + fates_seed_dispersal_fraction:units = "fraction" ; + fates_seed_dispersal_fraction:long_name = "fraction of seed rain to be dispersed to other grid cells" ; + double fates_seed_dispersal_max_dist(fates_pft) ; + fates_seed_dispersal_max_dist:units = "m" ; + fates_seed_dispersal_max_dist:long_name = "maximum seed dispersal distance for a given pft" ; + double fates_seed_dispersal_pdf_scale(fates_pft) ; + fates_seed_dispersal_pdf_scale:units = "unitless" ; + fates_seed_dispersal_pdf_scale:long_name = "seed dispersal probability density function scale parameter, A, Table 1 Bullock et al 2016" ; + double fates_seed_dispersal_pdf_shape(fates_pft) ; + fates_seed_dispersal_pdf_shape:units = "unitless" ; + fates_seed_dispersal_pdf_shape:long_name = "seed dispersal probability density function shape parameter, B, Table 1 Bullock et al 2016" ; + double fates_stoich_nitr(fates_plant_organs, fates_pft) ; + fates_stoich_nitr:units = "gN/gC" ; + fates_stoich_nitr:long_name = "target nitrogen concentration (ratio with carbon) of organs" ; + double fates_stoich_phos(fates_plant_organs, fates_pft) ; + fates_stoich_phos:units = "gP/gC" ; + fates_stoich_phos:long_name = "target phosphorus concentration (ratio with carbon) of organs" ; + double fates_trim_inc(fates_pft) ; + fates_trim_inc:units = "m2/m2" ; + fates_trim_inc:long_name = "Arbitrary incremental change in trimming function." ; + double fates_trim_limit(fates_pft) ; + fates_trim_limit:units = "m2/m2" ; + fates_trim_limit:long_name = "Arbitrary limit to reductions in leaf area with stress" ; + double fates_trs_repro_alloc_a(fates_pft) ; + fates_trs_repro_alloc_a:units = "fraction" ; + fates_trs_repro_alloc_a:long_name = "shape parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_alloc_b(fates_pft) ; + fates_trs_repro_alloc_b:units = "fraction" ; + fates_trs_repro_alloc_b:long_name = "intercept parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_frac_seed(fates_pft) ; + fates_trs_repro_frac_seed:units = "fraction" ; + fates_trs_repro_frac_seed:long_name = "fraction of reproductive mass that is seed" ; + double fates_trs_seedling_a_emerg(fates_pft) ; + fates_trs_seedling_a_emerg:units = "day -1" ; + fates_trs_seedling_a_emerg:long_name = "mean fraction of seed bank emerging" ; + double fates_trs_seedling_b_emerg(fates_pft) ; + fates_trs_seedling_b_emerg:units = "day -1" ; + fates_trs_seedling_b_emerg:long_name = "seedling emergence sensitivity to soil moisture" ; + double fates_trs_seedling_background_mort(fates_pft) ; + fates_trs_seedling_background_mort:units = "yr-1" ; + fates_trs_seedling_background_mort:long_name = "background seedling mortality rate" ; + double fates_trs_seedling_h2o_mort_a(fates_pft) ; + fates_trs_seedling_h2o_mort_a:units = "-" ; + fates_trs_seedling_h2o_mort_a:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_b(fates_pft) ; + fates_trs_seedling_h2o_mort_b:units = "-" ; + fates_trs_seedling_h2o_mort_b:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_c(fates_pft) ; + fates_trs_seedling_h2o_mort_c:units = "-" ; + fates_trs_seedling_h2o_mort_c:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_light_mort_a(fates_pft) ; + fates_trs_seedling_light_mort_a:units = "-" ; + fates_trs_seedling_light_mort_a:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_mort_b(fates_pft) ; + fates_trs_seedling_light_mort_b:units = "-" ; + fates_trs_seedling_light_mort_b:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_rec_a(fates_pft) ; + fates_trs_seedling_light_rec_a:units = "-" ; + fates_trs_seedling_light_rec_a:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_light_rec_b(fates_pft) ; + fates_trs_seedling_light_rec_b:units = "-" ; + fates_trs_seedling_light_rec_b:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_mdd_crit(fates_pft) ; + fates_trs_seedling_mdd_crit:units = "mm H2O day" ; + fates_trs_seedling_mdd_crit:long_name = "critical moisture deficit (suction) day accumulation for seedling moisture-based seedling mortality to begin" ; + double fates_trs_seedling_par_crit_germ(fates_pft) ; + fates_trs_seedling_par_crit_germ:units = "MJ m-2 day-1" ; + fates_trs_seedling_par_crit_germ:long_name = "critical light level for germination" ; + double fates_trs_seedling_psi_crit(fates_pft) ; + fates_trs_seedling_psi_crit:units = "mm H2O" ; + fates_trs_seedling_psi_crit:long_name = "critical soil moisture (suction) for seedling stress" ; + double fates_trs_seedling_psi_emerg(fates_pft) ; + fates_trs_seedling_psi_emerg:units = "mm h20 suction" ; + fates_trs_seedling_psi_emerg:long_name = "critical soil moisture for seedling emergence" ; + double fates_trs_seedling_root_depth(fates_pft) ; + fates_trs_seedling_root_depth:units = "m" ; + fates_trs_seedling_root_depth:long_name = "rooting depth of seedlings" ; + double fates_turb_displar(fates_pft) ; + fates_turb_displar:units = "unitless" ; + fates_turb_displar:long_name = "Ratio of displacement height to canopy top height" ; + double fates_turb_leaf_diameter(fates_pft) ; + fates_turb_leaf_diameter:units = "m" ; + fates_turb_leaf_diameter:long_name = "Characteristic leaf dimension" ; + double fates_turb_z0mr(fates_pft) ; + fates_turb_z0mr:units = "unitless" ; + fates_turb_z0mr:long_name = "Ratio of momentum roughness length to canopy top height" ; + double fates_turnover_branch(fates_pft) ; + fates_turnover_branch:units = "yr" ; + fates_turnover_branch:long_name = "turnover time of branches" ; + double fates_turnover_fnrt(fates_pft) ; + fates_turnover_fnrt:units = "yr" ; + fates_turnover_fnrt:long_name = "root longevity (alternatively, turnover time)" ; + double fates_turnover_leaf_canopy(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_canopy:units = "yr" ; + fates_turnover_leaf_canopy:long_name = "Leaf longevity (ie turnover timescale) of canopy plants. For drought-deciduous PFTs, this also indicates the maximum length of the growing (i.e., leaves on) season." ; + double fates_turnover_leaf_ustory(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_ustory:units = "yr" ; + fates_turnover_leaf_ustory:long_name = "Leaf longevity (ie turnover timescale) of understory plants." ; + double fates_turnover_senleaf_fdrought(fates_pft) ; + fates_turnover_senleaf_fdrought:units = "unitless[0-1]" ; + fates_turnover_senleaf_fdrought:long_name = "multiplication factor for leaf longevity of senescent leaves during drought" ; + double fates_wood_density(fates_pft) ; + fates_wood_density:units = "g/cm3" ; + fates_wood_density:long_name = "mean density of woody tissue in plant" ; + double fates_woody(fates_pft) ; + fates_woody:units = "logical flag" ; + fates_woody:long_name = "Binary woody lifeform flag" ; + double fates_hlm_pft_map(fates_hlm_pftno, fates_pft) ; + fates_hlm_pft_map:units = "area fraction" ; + fates_hlm_pft_map:long_name = "In fixed biogeog mode, fraction of HLM area associated with each FATES PFT" ; + double fates_fire_FBD(fates_litterclass) ; + fates_fire_FBD:units = "kg Biomass/m3" ; + fates_fire_FBD:long_name = "fuel bulk density" ; + double fates_fire_low_moisture_Coeff(fates_litterclass) ; + fates_fire_low_moisture_Coeff:units = "NA" ; + fates_fire_low_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_low_moisture_Slope(fates_litterclass) ; + fates_fire_low_moisture_Slope:units = "NA" ; + fates_fire_low_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture(fates_litterclass) ; + fates_fire_mid_moisture:units = "NA" ; + fates_fire_mid_moisture:long_name = "spitfire litter moisture threshold to be considered medium dry" ; + double fates_fire_mid_moisture_Coeff(fates_litterclass) ; + fates_fire_mid_moisture_Coeff:units = "NA" ; + fates_fire_mid_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture_Slope(fates_litterclass) ; + fates_fire_mid_moisture_Slope:units = "NA" ; + fates_fire_mid_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_min_moisture(fates_litterclass) ; + fates_fire_min_moisture:units = "NA" ; + fates_fire_min_moisture:long_name = "spitfire litter moisture threshold to be considered very dry" ; + double fates_fire_SAV(fates_litterclass) ; + fates_fire_SAV:units = "cm-1" ; + fates_fire_SAV:long_name = "fuel surface area to volume ratio" ; + double fates_frag_maxdecomp(fates_litterclass) ; + fates_frag_maxdecomp:units = "yr-1" ; + fates_frag_maxdecomp:long_name = "maximum rate of litter & CWD transfer from non-decomposing class into decomposing class" ; + double fates_frag_cwd_frac(fates_NCWD) ; + fates_frag_cwd_frac:units = "fraction" ; + fates_frag_cwd_frac:long_name = "fraction of woody (bdead+bsw) biomass destined for CWD pool" ; + double fates_landuse_crop_lu_pft_vector(fates_landuseclass) ; + fates_landuse_crop_lu_pft_vector:units = "NA" ; + fates_landuse_crop_lu_pft_vector:long_name = "the FATES PFT index to use on a given crop land-use type (dummy value of -999 for non-crop types)" ; + double fates_landuse_grazing_rate(fates_landuseclass) ; + fates_landuse_grazing_rate:units = "1/day" ; + fates_landuse_grazing_rate:long_name = "fraction of leaf biomass consumed by grazers per day" ; + double fates_max_nocomp_pfts_by_landuse(fates_landuseclass) ; + fates_max_nocomp_pfts_by_landuse:units = "count" ; + fates_max_nocomp_pfts_by_landuse:long_name = "maximum number of nocomp PFTs on each land use type (only used in nocomp mode)" ; + double fates_maxpatches_by_landuse(fates_landuseclass) ; + fates_maxpatches_by_landuse:units = "count" ; + fates_maxpatches_by_landuse:long_name = "maximum number of patches per site on each land use type" ; + double fates_canopy_closure_thresh ; + fates_canopy_closure_thresh:units = "unitless" ; + fates_canopy_closure_thresh:long_name = "tree canopy coverage at which crown area allometry changes from savanna to forest value" ; + double fates_cnp_eca_plant_escalar ; + fates_cnp_eca_plant_escalar:units = "" ; + fates_cnp_eca_plant_escalar:long_name = "scaling factor for plant fine root biomass to calculate nutrient carrier enzyme abundance (ECA)" ; + double fates_cohort_age_fusion_tol ; + fates_cohort_age_fusion_tol:units = "unitless" ; + fates_cohort_age_fusion_tol:long_name = "minimum fraction in differece in cohort age between cohorts." ; + double fates_cohort_size_fusion_tol ; + fates_cohort_size_fusion_tol:units = "unitless" ; + fates_cohort_size_fusion_tol:long_name = "minimum fraction in difference in dbh between cohorts" ; + double fates_comp_excln ; + fates_comp_excln:units = "none" ; + fates_comp_excln:long_name = "IF POSITIVE: weighting factor (exponent on dbh) for canopy layer exclusion and promotion, IF NEGATIVE: switch to use deterministic height sorting" ; + double fates_damage_canopy_layer_code ; + fates_damage_canopy_layer_code:units = "unitless" ; + fates_damage_canopy_layer_code:long_name = "Integer code that decides whether damage affects canopy trees (1), understory trees (2)" ; + double fates_damage_event_code ; + fates_damage_event_code:units = "unitless" ; + fates_damage_event_code:long_name = "Integer code that options how damage events are structured" ; + double fates_dev_arbitrary ; + fates_dev_arbitrary:units = "unknown" ; + fates_dev_arbitrary:long_name = "Unassociated free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_active_crown_fire ; + fates_fire_active_crown_fire:units = "0 or 1" ; + fates_fire_active_crown_fire:long_name = "flag, 1=active crown fire 0=no active crown fire" ; + double fates_fire_cg_strikes ; + fates_fire_cg_strikes:units = "fraction (0-1)" ; + fates_fire_cg_strikes:long_name = "fraction of cloud to ground lightning strikes" ; + double fates_fire_drying_ratio ; + fates_fire_drying_ratio:units = "NA" ; + fates_fire_drying_ratio:long_name = "spitfire parameter, fire drying ratio for fuel moisture, alpha_FMC EQ 6 Thonicke et al 2010" ; + double fates_fire_durat_slope ; + fates_fire_durat_slope:units = "NA" ; + fates_fire_durat_slope:long_name = "spitfire parameter, fire max duration slope, Equation 14 Thonicke et al 2010" ; + double fates_fire_fdi_alpha ; + fates_fire_fdi_alpha:units = "NA" ; + fates_fire_fdi_alpha:long_name = "spitfire parameter, EQ 7 Venevsky et al. GCB 2002,(modified EQ 8 Thonicke et al. 2010) " ; + double fates_fire_fuel_energy ; + fates_fire_fuel_energy:units = "kJ/kg" ; + fates_fire_fuel_energy:long_name = "spitfire parameter, heat content of fuel" ; + double fates_fire_max_durat ; + fates_fire_max_durat:units = "minutes" ; + fates_fire_max_durat:long_name = "spitfire parameter, fire maximum duration, Equation 14 Thonicke et al 2010" ; + double fates_fire_miner_damp ; + fates_fire_miner_damp:units = "NA" ; + fates_fire_miner_damp:long_name = "spitfire parameter, mineral-dampening coefficient EQ A1 Thonicke et al 2010 " ; + double fates_fire_miner_total ; + fates_fire_miner_total:units = "fraction" ; + fates_fire_miner_total:long_name = "spitfire parameter, total mineral content, Table A1 Thonicke et al 2010" ; + double fates_fire_nignitions ; + fates_fire_nignitions:units = "ignitions per year per km2" ; + fates_fire_nignitions:long_name = "number of annual ignitions per square km" ; + double fates_fire_part_dens ; + fates_fire_part_dens:units = "kg/m2" ; + fates_fire_part_dens:long_name = "spitfire parameter, oven dry particle density, Table A1 Thonicke et al 2010" ; + double fates_fire_threshold ; + fates_fire_threshold:units = "kW/m" ; + fates_fire_threshold:long_name = "spitfire parameter, fire intensity threshold for tracking fires that spread" ; + double fates_frag_cwd_fcel ; + fates_frag_cwd_fcel:units = "unitless" ; + fates_frag_cwd_fcel:long_name = "Cellulose fraction for CWD" ; + double fates_frag_cwd_flig ; + fates_frag_cwd_flig:units = "unitless" ; + fates_frag_cwd_flig:long_name = "Lignin fraction of coarse woody debris" ; + double fates_hydro_kmax_rsurf1 ; + fates_hydro_kmax_rsurf1:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf1:long_name = "maximum conducitivity for unit root surface (into root)" ; + double fates_hydro_kmax_rsurf2 ; + fates_hydro_kmax_rsurf2:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf2:long_name = "maximum conducitivity for unit root surface (out of root)" ; + double fates_hydro_psi0 ; + fates_hydro_psi0:units = "MPa" ; + fates_hydro_psi0:long_name = "sapwood water potential at saturation" ; + double fates_hydro_psicap ; + fates_hydro_psicap:units = "MPa" ; + fates_hydro_psicap:long_name = "sapwood water potential at which capillary reserves exhausted" ; + double fates_landuse_grazing_carbon_use_eff ; + fates_landuse_grazing_carbon_use_eff:units = "unitless" ; + fates_landuse_grazing_carbon_use_eff:long_name = "carbon use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_maxheight ; + fates_landuse_grazing_maxheight:units = "m" ; + fates_landuse_grazing_maxheight:long_name = "maximum height that grazers (browsers, actually) can reach" ; + double fates_landuse_grazing_nitrogen_use_eff ; + fates_landuse_grazing_nitrogen_use_eff:units = "unitless" ; + fates_landuse_grazing_nitrogen_use_eff:long_name = "nitrogen use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_phosphorus_use_eff ; + fates_landuse_grazing_phosphorus_use_eff:units = "unitless" ; + fates_landuse_grazing_phosphorus_use_eff:long_name = "phosphorus use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_logging_coll_under_frac ; + fates_landuse_logging_coll_under_frac:units = "fraction" ; + fates_landuse_logging_coll_under_frac:long_name = "Fraction of stems killed in the understory when logging generates disturbance" ; + double fates_landuse_logging_collateral_frac ; + fates_landuse_logging_collateral_frac:units = "fraction" ; + fates_landuse_logging_collateral_frac:long_name = "Fraction of large stems in upperstory that die from logging collateral damage" ; + double fates_landuse_logging_dbhmax ; + fates_landuse_logging_dbhmax:units = "cm" ; + fates_landuse_logging_dbhmax:long_name = "Maximum dbh below which logging is applied (unset values flag this to be unused)" ; + double fates_landuse_logging_dbhmax_infra ; + fates_landuse_logging_dbhmax_infra:units = "cm" ; + fates_landuse_logging_dbhmax_infra:long_name = "Tree diameter, above which infrastructure from logging does not impact damage or mortality." ; + double fates_landuse_logging_dbhmin ; + fates_landuse_logging_dbhmin:units = "cm" ; + fates_landuse_logging_dbhmin:long_name = "Minimum dbh at which logging is applied" ; + double fates_landuse_logging_direct_frac ; + fates_landuse_logging_direct_frac:units = "fraction" ; + fates_landuse_logging_direct_frac:long_name = "Fraction of stems logged directly per event" ; + double fates_landuse_logging_event_code ; + fates_landuse_logging_event_code:units = "unitless" ; + fates_landuse_logging_event_code:long_name = "Integer code that options how logging events are structured" ; + double fates_landuse_logging_export_frac ; + fates_landuse_logging_export_frac:units = "fraction" ; + fates_landuse_logging_export_frac:long_name = "fraction of trunk product being shipped offsite, the leftovers will be left onsite as large CWD" ; + double fates_landuse_logging_mechanical_frac ; + fates_landuse_logging_mechanical_frac:units = "fraction" ; + fates_landuse_logging_mechanical_frac:long_name = "Fraction of stems killed due infrastructure an other mechanical means" ; + double fates_leaf_photo_temp_acclim_thome_time ; + fates_leaf_photo_temp_acclim_thome_time:units = "years" ; + fates_leaf_photo_temp_acclim_thome_time:long_name = "Length of the window for the long-term (i.e. T_home in Kumarathunge et al 2019) exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_photo_temp_acclim_timescale ; + fates_leaf_photo_temp_acclim_timescale:units = "days" ; + fates_leaf_photo_temp_acclim_timescale:long_name = "Length of the window for the exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_maintresp_leaf_model=2 or fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_theta_cj_c3 ; + fates_leaf_theta_cj_c3:units = "unitless" ; + fates_leaf_theta_cj_c3:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_leaf_theta_cj_c4 ; + fates_leaf_theta_cj_c4:units = "unitless" ; + fates_leaf_theta_cj_c4:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_maintresp_nonleaf_baserate ; + fates_maintresp_nonleaf_baserate:units = "gC/gN/s" ; + fates_maintresp_nonleaf_baserate:long_name = "Base maintenance respiration rate for plant tissues, using Ryan 1991" ; + double fates_maxcohort ; + fates_maxcohort:units = "count" ; + fates_maxcohort:long_name = "maximum number of cohorts per patch. Actual number of cohorts also depend on cohort fusion tolerances" ; + double fates_mort_disturb_frac ; + fates_mort_disturb_frac:units = "fraction" ; + fates_mort_disturb_frac:long_name = "fraction of canopy mortality that results in disturbance (i.e. transfer of area from new to old patch)" ; + double fates_mort_understorey_death ; + fates_mort_understorey_death:units = "fraction" ; + fates_mort_understorey_death:long_name = "fraction of plants in understorey cohort impacted by overstorey tree-fall" ; + double fates_patch_fusion_tol ; + fates_patch_fusion_tol:units = "unitless" ; + fates_patch_fusion_tol:long_name = "minimum fraction in difference in profiles between patches" ; + double fates_phen_chilltemp ; + fates_phen_chilltemp:units = "degrees C" ; + fates_phen_chilltemp:long_name = "chilling day counting threshold for vegetation" ; + double fates_phen_coldtemp ; + fates_phen_coldtemp:units = "degrees C" ; + fates_phen_coldtemp:long_name = "vegetation temperature exceedance that flags a cold-day for leaf-drop" ; + double fates_phen_gddthresh_a ; + fates_phen_gddthresh_a:units = "none" ; + fates_phen_gddthresh_a:long_name = "GDD accumulation function, intercept parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_b ; + fates_phen_gddthresh_b:units = "none" ; + fates_phen_gddthresh_b:long_name = "GDD accumulation function, multiplier parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_c ; + fates_phen_gddthresh_c:units = "none" ; + fates_phen_gddthresh_c:long_name = "GDD accumulation function, exponent parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_mindayson ; + fates_phen_mindayson:units = "days" ; + fates_phen_mindayson:long_name = "day threshold compared against days since leaves became on-allometry" ; + double fates_phen_ncolddayslim ; + fates_phen_ncolddayslim:units = "days" ; + fates_phen_ncolddayslim:long_name = "day threshold exceedance for temperature leaf-drop" ; + double fates_q10_froz ; + fates_q10_froz:units = "unitless" ; + fates_q10_froz:long_name = "Q10 for frozen-soil respiration rates" ; + double fates_q10_mr ; + fates_q10_mr:units = "unitless" ; + fates_q10_mr:long_name = "Q10 for maintenance respiration" ; + double fates_soil_salinity ; + fates_soil_salinity:units = "ppt" ; + fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; + double fates_trs_seedling2sap_par_timescale ; + fates_trs_seedling2sap_par_timescale:units = "days" ; + fates_trs_seedling2sap_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling to sapling transition rates" ; + double fates_trs_seedling_emerg_h2o_timescale ; + fates_trs_seedling_emerg_h2o_timescale:units = "days" ; + fates_trs_seedling_emerg_h2o_timescale:long_name = "Length of the window for the exponential moving average of smp used to calculate seedling emergence" ; + double fates_trs_seedling_mdd_timescale ; + fates_trs_seedling_mdd_timescale:units = "days" ; + fates_trs_seedling_mdd_timescale:long_name = "Length of the window for the exponential moving average of moisture deficit days used to calculate seedling mortality" ; + double fates_trs_seedling_mort_par_timescale ; + fates_trs_seedling_mort_par_timescale:units = "days" ; + fates_trs_seedling_mort_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling mortality" ; + double fates_vai_top_bin_width ; + fates_vai_top_bin_width:units = "m2/m2" ; + fates_vai_top_bin_width:long_name = "width in VAI units of uppermost leaf+stem layer scattering element in each canopy layer" ; + double fates_vai_width_increase_factor ; + fates_vai_width_increase_factor:units = "unitless" ; + fates_vai_width_increase_factor:long_name = "factor by which each leaf+stem scattering element increases in VAI width (1 = uniform spacing)" ; + +// global attributes: + :history = "This file was generated by BatchPatchParams.py:\nCDL Base File = fates_params_default.cdl\nXML patch file = archive/api36.1.0_100224_pr1255-2.xml" ; +data: + + fates_history_ageclass_bin_edges = 0, 1, 2, 5, 10, 20, 50 ; + + fates_history_coageclass_bin_edges = 0, 5 ; + + fates_history_height_bin_edges = 0, 0.1, 0.3, 1, 3, 10 ; + + fates_history_damage_bin_edges = 0, 80 ; + + fates_history_sizeclass_bin_edges = 0, 5, 10, 15, 20, 30, 40, 50, 60, 70, + 80, 90, 100 ; + + fates_alloc_organ_id = 1, 2, 3, 6 ; + + fates_hydro_htftype_node = 1, 1, 1, 1 ; + + fates_pftname = + "broadleaf_evergreen_tropical_tree ", + "needleleaf_evergreen_extratrop_tree ", + "needleleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_tree ", + "broadleaf_hydrodecid_tropical_tree ", + "broadleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_shrub ", + "broadleaf_hydrodecid_extratrop_shrub ", + "broadleaf_colddecid_extratrop_shrub ", + " broadleaf_evergreen_arctic_shrub ", + " broadleaf_colddecid_arctic_shrub ", + "arctic_c3_grass ", + "cool_c3_grass ", + "c4_grass " ; + + fates_hydro_organ_name = + "leaf ", + "stem ", + "transporting root ", + "absorbing root " ; + + fates_alloc_organ_name = + "leaf", + "fine root", + "sapwood", + "structure" ; + + fates_landuseclass_name = + "primaryland", + "secondaryland", + "rangeland", + "pastureland", + "cropland" ; + + fates_litterclass_name = + "twig ", + "small branch ", + "large branch ", + "trunk ", + "dead leaves ", + "live grass " ; + + fates_alloc_organ_priority = + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; + + fates_alloc_storage_cushion = 1.2, 1.2, 1.2, 1.2, 2.4, 1.2, 1.2, 2.4, 1.2, + 1.5, 1.4, 1.2, 1.2, 1.2 ; + + fates_alloc_store_priority_frac = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.7, 0.6, 0.6, 0.8, 0.8 ; + + fates_allom_agb1 = 0.0673, 0.1364012, 0.0393057, 0.2653695, 0.0673, + 0.0728698, 0.06896, 0.06896, 0.06896, 0.06896, 0.06896, 0.001, 0.001, + 0.003 ; + + fates_allom_agb2 = 0.976, 0.9449041, 1.087335, 0.8321321, 0.976, 1.0373211, + 0.572, 0.572, 0.572, 0.5289883, 0.6853945, 1.6592, 1.6592, 1.3456 ; + + fates_allom_agb3 = 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, + 2.1010352, 1.7628613, 1.248, 1.248, 1.869 ; + + fates_allom_agb4 = 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, + 0.931, 0.931, 0.931, -999.9, -999.9, -999.9 ; + + fates_allom_agb_frac = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 1, 1, 1 ; + + fates_allom_amode = 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_blca_expnt_diff = -0.12, -0.34, -0.32, -0.22, -0.12, -0.35, 0, + 0, 0, 0, 0, -0.487, -0.487, -0.259 ; + + fates_allom_cmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_d2bl1 = 0.04, 0.07, 0.07, 0.01, 0.04, 0.07, 0.07, 0.07, 0.07, + 0.0481934, 0.0481934, 0.0004, 0.0004, 0.0012 ; + + fates_allom_d2bl2 = 1.6019679, 1.5234373, 1.3051237, 1.9621397, 1.6019679, + 1.3998939, 1.3, 1.3, 1.3, 1.0600586, 1.7176758, 1.7092, 1.7092, 1.5879 ; + + fates_allom_d2bl3 = 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, + 0.55, 0.55, 0.3417, 0.3417, 0.9948 ; + + fates_allom_d2ca_coefficient_max = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2ca_coefficient_min = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2h1 = 78.4087704, 306.842667, 106.8745821, 104.3586841, + 78.4087704, 31.4557047, 0.64, 0.64, 0.64, 0.8165625, 0.778125, 0.1812, + 0.1812, 0.3353 ; + + fates_allom_d2h2 = 0.8124383, 0.752377, 0.9471302, 1.1146973, 0.8124383, + 0.9734088, 0.37, 0.37, 0.37, 0.2316113, 0.4027002, 0.6384, 0.6384, 0.4235 ; + + fates_allom_d2h3 = 47.6666164, 196.6865691, 93.9790461, 160.6835089, + 47.6666164, 16.5928174, -999.9, -999.9, -999.9, -999.9, -999.9, -999.9, + -999.9, -999.9 ; + + fates_allom_dbh_maxheight = 1000, 1000, 1000, 1000, 1000, 1000, 3, 3, 2, + 2.4, 1.9, 20, 20, 30 ; + + fates_allom_dmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fnrt_prof_a = 7, 7, 7, 7, 6, 6, 7, 7, 7, 7, 7, 11, 11, 11 ; + + fates_allom_fnrt_prof_b = 1, 2, 2, 1, 2, 2, 1.5, 1.5, 1.5, 1.5, 1.5, 2, 2, 2 ; + + fates_allom_fnrt_prof_mode = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 ; + + fates_allom_frbstor_repro = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_h2cd1 = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.95, 0.95, 0.95, 0.95, + 0.95, 1, 1, 1 ; + + fates_allom_h2cd2 = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_hmode = 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 3, 3, 3 ; + + fates_allom_l2fr = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.67, 0.67, 1.41 ; + + fates_allom_la_per_sa_int = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.8, 0.8, 0.8, 0.8 ; + + fates_allom_la_per_sa_slp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_lmode = 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_sai_scaler = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1 ; + + fates_allom_smode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 ; + + fates_allom_stmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_zroot_k = 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ; + + fates_allom_zroot_max_dbh = 100, 100, 100, 100, 100, 100, 2, 2, 2, 2, 2, 2, + 2, 2 ; + + fates_allom_zroot_max_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_allom_zroot_min_dbh = 1, 1, 1, 2.5, 2.5, 2.5, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_allom_zroot_min_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_c2b = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_cnp_eca_alpha_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_decompmicc = 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280 ; + + fates_cnp_eca_km_nh4 = 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, + 0.14, 0.14, 0.14, 0.14, 0.14 ; + + fates_cnp_eca_km_no3 = 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, + 0.27, 0.27, 0.27, 0.27, 0.27 ; + + fates_cnp_eca_km_p = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_eca_km_ptase = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_eca_lambda_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_vmax_ptase = 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, + 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09 ; + + fates_cnp_nfix1 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_nitr_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_phos_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_pid_kd = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_pid_ki = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_pid_kp = 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, + 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005 ; + + fates_cnp_prescribed_nuptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_prescribed_puptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_store_ovrflw_frac = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_turnover_nitr_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_turnover_phos_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_vmax_nh4 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_no3 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_p = 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, + 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10 ; + + fates_damage_frac = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_damage_mort_p1 = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 ; + + fates_damage_mort_p2 = 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.5 ; + + fates_damage_recovery_scalar = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_dev_arbitrary_pft = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_fire_alpha_SH = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2 ; + + fates_fire_bark_scaler = 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, + 0.07, 0.07, 0.07, 0.07, 0.07, 0.07 ; + + fates_fire_crown_kill = 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, + 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775 ; + + fates_frag_fnrt_fcel = 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_frag_fnrt_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_fnrt_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_fcel = 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_frag_leaf_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_seed_decay_rate = 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, + 0.51, 0.74, 0.46, 0.35, 0.51, 0.51 ; + + fates_grperc = 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.12, + 0.11, 0.16, 0.11, 0.11 ; + + fates_hydro_avuln_gs = 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, + 2.5, 2.5, 2.5, 2.5 ; + + fates_hydro_avuln_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_hydro_epsil_node = + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_hydro_fcap_node = + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_k_lwp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_kmax_node = + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999 ; + + fates_hydro_p50_gs = -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, + -1.5, -1.5, -1.5, -1.5, -1.5 ; + + fates_hydro_p50_node = + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25 ; + + fates_hydro_p_taper = 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333 ; + + fates_hydro_pinot_node = + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478 ; + + fates_hydro_pitlp_node = + -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, + -1.67, -1.67, -1.67, -1.67, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, + -1.2, -1.2 ; + + fates_hydro_resid_node = + 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, + 0.16, 0.16, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, + 0.11, 0.11 ; + + fates_hydro_rfrac_stem = 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625 ; + + fates_hydro_rs2 = 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, + 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001 ; + + fates_hydro_srl = 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25 ; + + fates_hydro_thetas_node = + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75 ; + + fates_hydro_vg_alpha_node = + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12 ; + + fates_hydro_vg_m_node = + 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, + 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, + 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, + 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_hydro_vg_n_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_landuse_grazing_palatability = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 ; + + fates_landuse_harvest_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, + 1, 1, 1 ; + + fates_landuse_luc_frac_burned = 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_landuse_luc_frac_exported = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.2, 0.2, + 0.2, 0.2, 0.2, 0, 0, 0 ; + + fates_landuse_luc_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, 1, + 1, 1 ; + + fates_leaf_agross_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_c3psn = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 ; + + fates_leaf_fnps = 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, + 0.15, 0.15, 0.15, 0.15, 0.15 ; + + fates_leaf_jmaxha = 43540, 43540, 43540, 43540, 43540, 43540, 43540, 43540, + 43540, 43540, 43540, 43540, 43540, 43540 ; + + fates_leaf_jmaxhd = 152040, 152040, 152040, 152040, 152040, 152040, 152040, + 152040, 152040, 152040, 152040, 152040, 152040, 152040 ; + + fates_leaf_jmaxse = 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495 ; + + fates_leaf_slamax = 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.012, + 0.03, 0.03, 0.012, 0.032, 0.05, 0.05, 0.05 ; + + fates_leaf_slatop = 0.012, 0.005, 0.024, 0.009, 0.03, 0.03, 0.012, 0.03, + 0.03, 0.01, 0.032, 0.027, 0.05, 0.05 ; + + fates_leaf_stomatal_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_stomatal_intercept = 10000, 10000, 10000, 10000, 10000, 10000, + 10000, 10000, 10000, 10000, 10000, 10000, 10000, 40000 ; + + fates_leaf_stomatal_slope_ballberry = 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_leaf_stomatal_slope_medlyn = 4.1, 2.3, 2.3, 4.1, 4.4, 4.4, 4.7, 4.7, + 4.7, 4.7, 4.7, 2.2, 5.3, 1.6 ; + + fates_leaf_vcmax25top = + 50, 62, 39, 61, 58, 58, 62, 54, 54, 38, 54, 86, 78, 78 ; + + fates_leaf_vcmaxha = 65330, 65330, 65330, 65330, 65330, 65330, 65330, 65330, + 65330, 65330, 65330, 65330, 65330, 65330 ; + + fates_leaf_vcmaxhd = 149250, 149250, 149250, 149250, 149250, 149250, 149250, + 149250, 149250, 149250, 149250, 149250, 149250, 149250 ; + + fates_leaf_vcmaxse = 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, + 485, 485, 485 ; + + fates_leafn_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963 ; + + fates_leafn_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_leaf_atkin2017_baserate = 1.756, 1.4995, 1.4995, 1.756, + 1.756, 1.756, 2.0749, 2.0749, 2.0749, 2.0749, 2.0749, 2.1956, 2.1956, + 2.1956 ; + + fates_maintresp_leaf_ryan1991_baserate = 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06 ; + + fates_maintresp_leaf_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963 ; + + fates_maintresp_leaf_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_reduction_curvature = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_maintresp_reduction_intercept = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_maintresp_reduction_upthresh = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_mort_bmort = 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, + 0.014, 0.016, 0.01, 0.014, 0.014, 0.014 ; + + 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, + 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 = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_prescribed_canopy = 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, + 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194 ; + + fates_mort_prescribed_understory = 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, + 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025 ; + + fates_mort_r_age_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_r_size_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_scalar_coldstress = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3.5, 2.3, 3, 3 ; + + fates_mort_scalar_cstarvation = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.57, 0.6, 0.6, 0.6 ; + + fates_mort_scalar_hydrfailure = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.8, 0.6, 0.6, 0.6 ; + + fates_mort_upthresh_cstarvation = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_nonhydro_smpsc = -255000, -255000, -255000, -255000, -255000, -255000, + -255000, -255000, -255000, -255000, -255000, -255000, -255000, -255000 ; + + fates_nonhydro_smpso = -66000, -66000, -66000, -66000, -66000, -66000, + -66000, -66000, -66000, -66000, -66000, -66000, -66000, -66000 ; + + fates_phen_cold_size_threshold = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_drought_threshold = -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4 ; + + fates_phen_evergreen = 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 ; + + fates_phen_flush_fraction = _, _, 0.5, _, 0.5, 0.5, _, 0.5, 0.5, _, 0.5, + 0.5, 0.5, 0.5 ; + + fates_phen_fnrt_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_mindaysoff = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_phen_moist_threshold = -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9 ; + + fates_phen_season_decid = 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0 ; + + fates_phen_stem_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_stress_decid = 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1 ; + + fates_prescribed_npp_canopy = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4, 0.4, 0.4, 0.4 ; + + fates_prescribed_npp_understory = 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125 ; + + fates_rad_leaf_clumping_index = 0.85, 0.85, 0.8, 0.85, 0.85, 0.9, 0.85, 0.9, + 0.9, 0.85, 0.9, 0.75, 0.75, 0.75 ; + + fates_rad_leaf_rhonir = 0.46, 0.41, 0.39, 0.46, 0.41, 0.41, 0.46, 0.41, + 0.41, 0.46, 0.41, 0.28, 0.28, 0.28 ; + + fates_rad_leaf_rhovis = 0.11, 0.09, 0.08, 0.11, 0.08, 0.08, 0.11, 0.08, + 0.08, 0.11, 0.08, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_taunir = 0.33, 0.32, 0.42, 0.33, 0.43, 0.43, 0.33, 0.43, + 0.43, 0.33, 0.43, 0.4, 0.4, 0.4 ; + + fates_rad_leaf_tauvis = 0.06, 0.04, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_xl = 0.32, 0.01, 0.01, 0.32, 0.2, 0.59, 0.32, 0.59, 0.59, + 0.32, 0.59, -0.23, -0.23, -0.23 ; + + fates_rad_stem_rhonir = 0.49, 0.36, 0.36, 0.49, 0.49, 0.49, 0.49, 0.49, + 0.49, 0.49, 0.49, 0.53, 0.53, 0.53 ; + + fates_rad_stem_rhovis = 0.21, 0.12, 0.12, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, 0.21, 0.31, 0.31, 0.31 ; + + fates_rad_stem_taunir = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.25, 0.25, 0.25 ; + + fates_rad_stem_tauvis = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.12, 0.12, 0.12 ; + + fates_recruit_height_min = 1.3, 1.3, 1.3, 1.3, 1.3, 1.3, 0.2, 0.2, 0.2, 0.8, + 0.8, 0.11, 0.2, 0.2 ; + + fates_recruit_init_density = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.16, 0.2, 0.2, 0.2, 0.2 ; + + fates_recruit_prescribed_rate = 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02 ; + + fates_recruit_seed_alloc = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.07, 0.1, 0, 0, 0 ; + + fates_recruit_seed_alloc_mature = 0, 0, 0, 0, 0, 0, 0.9, 0.9, 0.9, 0.9, 0.9, + 0.25, 0.25, 0.2 ; + + fates_recruit_seed_dbh_repro_threshold = 90, 80, 80, 80, 90, 80, 3, 3, 2, + 2.4, 1.9, 3, 3, 3 ; + + fates_recruit_seed_germination_rate = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.4, 0.49, 0.29, 0.5, 0.5 ; + + fates_recruit_seed_supplement = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_seed_dispersal_fraction = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_max_dist = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_scale = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_shape = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_stoich_nitr = + 0.033, 0.029, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, + 0.04, 0.04, 0.04, + 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, + 0.024, 0.024, 0.024, 0.024, + 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, + 1e-08, 1e-08, 1e-08, 1e-08, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047 ; + + fates_stoich_phos = + 0.0033, 0.0029, 0.004, 0.0033, 0.004, 0.004, 0.0033, 0.004, 0.004, 0.0033, + 0.004, 0.004, 0.004, 0.004, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, + 1e-09, 1e-09, 1e-09, 1e-09, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047 ; + + fates_trim_inc = 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, + 0.03, 0.03, 0.03, 0.03 ; + + fates_trim_limit = 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.3, 0.3, 0.3 ; + + fates_trs_repro_alloc_a = 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, + 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049 ; + + fates_trs_repro_alloc_b = -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171 ; + + fates_trs_repro_frac_seed = 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, + 0.24, 0.24, 0.24, 0.24, 0.24, 0.24 ; + + fates_trs_seedling_a_emerg = 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, + 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003 ; + + fates_trs_seedling_b_emerg = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.2, 1.2, 1.2 ; + + fates_trs_seedling_background_mort = 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371 ; + + fates_trs_seedling_h2o_mort_a = 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17 ; + + fates_trs_seedling_h2o_mort_b = -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11 ; + + fates_trs_seedling_h2o_mort_c = 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05 ; + + fates_trs_seedling_light_mort_a = -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694 ; + + fates_trs_seedling_light_mort_b = -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063 ; + + fates_trs_seedling_light_rec_a = 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, + 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007 ; + + fates_trs_seedling_light_rec_b = 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, + 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615 ; + + fates_trs_seedling_mdd_crit = 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000 ; + + fates_trs_seedling_par_crit_germ = 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, + 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656 ; + + fates_trs_seedling_psi_crit = -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7 ; + + fates_trs_seedling_psi_emerg = -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65 ; + + fates_trs_seedling_root_depth = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06 ; + + fates_turb_displar = 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, + 0.67, 0.67, 0.67, 0.67, 0.67 ; + + fates_turb_leaf_diameter = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + 0.04, 0.04, 0.04, 0.04, 0.04, 0.04 ; + + fates_turb_z0mr = 0.075, 0.055, 0.055, 0.075, 0.055, 0.055, 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12 ; + + fates_turnover_branch = 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 0, 0, 0 ; + + fates_turnover_fnrt = 1, 2, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_canopy = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_ustory = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_senleaf_fdrought = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_wood_density = 0.548327, 0.44235, 0.454845, 0.754336, 0.548327, + 0.566452, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7 ; + + fates_woody = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ; + + fates_hlm_pft_map = + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0.1, 0.8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ; + + fates_fire_FBD = 15.4, 16.8, 19.6, 999, 4, 4 ; + + fates_fire_low_moisture_Coeff = 1.12, 1.09, 0.98, 0.8, 1.15, 1.15 ; + + fates_fire_low_moisture_Slope = 0.62, 0.72, 0.85, 0.8, 0.62, 0.62 ; + + fates_fire_mid_moisture = 0.72, 0.51, 0.38, 1, 0.8, 0.8 ; + + fates_fire_mid_moisture_Coeff = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_mid_moisture_Slope = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_min_moisture = 0.18, 0.12, 0, 0, 0.24, 0.24 ; + + fates_fire_SAV = 13, 3.58, 0.98, 0.2, 66, 66 ; + + fates_frag_maxdecomp = 0.52, 0.383, 0.383, 0.19, 1, 999 ; + + fates_frag_cwd_frac = 0.045, 0.075, 0.21, 0.67 ; + + fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 11 ; + + fates_landuse_grazing_rate = 0, 0, 0, 0, 0 ; + + fates_max_nocomp_pfts_by_landuse = 4, 4, 1, 1, 1 ; + + fates_maxpatches_by_landuse = 9, 4, 1, 1, 1 ; + + fates_canopy_closure_thresh = 0.8 ; + + fates_cnp_eca_plant_escalar = 1.25e-05 ; + + fates_cohort_age_fusion_tol = 0.08 ; + + fates_cohort_size_fusion_tol = 0.08 ; + + fates_comp_excln = 3 ; + + fates_damage_canopy_layer_code = 1 ; + + fates_damage_event_code = 1 ; + + fates_dev_arbitrary = _ ; + + fates_fire_active_crown_fire = 0 ; + + fates_fire_cg_strikes = 0.2 ; + + fates_fire_drying_ratio = 66000 ; + + fates_fire_durat_slope = -11.06 ; + + fates_fire_fdi_alpha = 0.00037 ; + + fates_fire_fuel_energy = 18000 ; + + fates_fire_max_durat = 240 ; + + fates_fire_miner_damp = 0.41739 ; + + fates_fire_miner_total = 0.055 ; + + fates_fire_nignitions = 15 ; + + fates_fire_part_dens = 513 ; + + fates_fire_threshold = 50 ; + + fates_frag_cwd_fcel = 0.76 ; + + fates_frag_cwd_flig = 0.24 ; + + fates_hydro_kmax_rsurf1 = 20 ; + + fates_hydro_kmax_rsurf2 = 0.0001 ; + + fates_hydro_psi0 = 0 ; + + fates_hydro_psicap = -0.6 ; + + fates_landuse_grazing_carbon_use_eff = 0 ; + + fates_landuse_grazing_maxheight = 1 ; + + fates_landuse_grazing_nitrogen_use_eff = 0.25 ; + + fates_landuse_grazing_phosphorus_use_eff = 0.5 ; + + fates_landuse_logging_coll_under_frac = 0.55983 ; + + fates_landuse_logging_collateral_frac = 0.05 ; + + fates_landuse_logging_dbhmax = _ ; + + fates_landuse_logging_dbhmax_infra = 35 ; + + fates_landuse_logging_dbhmin = 50 ; + + fates_landuse_logging_direct_frac = 0.15 ; + + fates_landuse_logging_event_code = -30 ; + + fates_landuse_logging_export_frac = 0.8 ; + + fates_landuse_logging_mechanical_frac = 0.05 ; + + fates_leaf_photo_temp_acclim_thome_time = 30 ; + + fates_leaf_photo_temp_acclim_timescale = 30 ; + + fates_leaf_theta_cj_c3 = 0.999 ; + + fates_leaf_theta_cj_c4 = 0.999 ; + + fates_maintresp_nonleaf_baserate = 2.525e-06 ; + + fates_maxcohort = 100 ; + + fates_mort_disturb_frac = 1 ; + + fates_mort_understorey_death = 0.55983 ; + + fates_patch_fusion_tol = 0.05 ; + + fates_phen_chilltemp = 5 ; + + fates_phen_coldtemp = 7.5 ; + + fates_phen_gddthresh_a = -68 ; + + fates_phen_gddthresh_b = 638 ; + + fates_phen_gddthresh_c = -0.01 ; + + fates_phen_mindayson = 90 ; + + fates_phen_ncolddayslim = 5 ; + + fates_q10_froz = 1.5 ; + + fates_q10_mr = 1.5 ; + + fates_soil_salinity = 0.4 ; + + fates_trs_seedling2sap_par_timescale = 32 ; + + fates_trs_seedling_emerg_h2o_timescale = 7 ; + + fates_trs_seedling_mdd_timescale = 126 ; + + fates_trs_seedling_mort_par_timescale = 32 ; + + fates_vai_top_bin_width = 1 ; + + fates_vai_width_increase_factor = 1 ; +} diff --git a/parameter_files/archive/api40.0.0_060625_params_default.cdl b/parameter_files/archive/api40.0.0_060625_params_default.cdl new file mode 100644 index 0000000000..9fb97c811f --- /dev/null +++ b/parameter_files/archive/api40.0.0_060625_params_default.cdl @@ -0,0 +1,1844 @@ +netcdf fates_params_default { +dimensions: + fates_NCWD = 4 ; + fates_history_age_bins = 7 ; + fates_history_coage_bins = 2 ; + fates_history_damage_bins = 2 ; + fates_history_height_bins = 6 ; + fates_history_size_bins = 13 ; + fates_hlm_pftno = 14 ; + fates_hydr_organs = 4 ; + fates_landuseclass = 5 ; + fates_leafage_class = 1 ; + fates_litterclass = 6 ; + fates_pft = 14 ; + fates_plant_organs = 4 ; + fates_string_length = 60 ; +variables: + double fates_history_ageclass_bin_edges(fates_history_age_bins) ; + fates_history_ageclass_bin_edges:units = "yr" ; + fates_history_ageclass_bin_edges:long_name = "Lower edges for age class bins used in age-resolved patch history output" ; + double fates_history_coageclass_bin_edges(fates_history_coage_bins) ; + fates_history_coageclass_bin_edges:units = "years" ; + fates_history_coageclass_bin_edges:long_name = "Lower edges for cohort age class bins used in cohort age resolved history output" ; + double fates_history_height_bin_edges(fates_history_height_bins) ; + fates_history_height_bin_edges:units = "m" ; + fates_history_height_bin_edges:long_name = "Lower edges for height bins used in height-resolved history output" ; + double fates_history_damage_bin_edges(fates_history_damage_bins) ; + fates_history_damage_bin_edges:units = "% crown loss" ; + fates_history_damage_bin_edges:long_name = "Lower edges for damage class bins used in cohort history output" ; + double fates_history_sizeclass_bin_edges(fates_history_size_bins) ; + fates_history_sizeclass_bin_edges:units = "cm" ; + fates_history_sizeclass_bin_edges:long_name = "Lower edges for DBH size class bins used in size-resolved cohort history output" ; + double fates_alloc_organ_id(fates_plant_organs) ; + fates_alloc_organ_id:units = "unitless" ; + fates_alloc_organ_id:long_name = "This is the global index that the organ in this file is associated with, values match those in parteh/PRTGenericMod.F90" ; + double fates_hydro_htftype_node(fates_hydr_organs) ; + fates_hydro_htftype_node:units = "unitless" ; + fates_hydro_htftype_node:long_name = "Switch that defines the hydraulic transfer functions for each organ." ; + char fates_pftname(fates_pft, fates_string_length) ; + fates_pftname:units = "unitless - string" ; + fates_pftname:long_name = "Description of plant type" ; + char fates_hydro_organ_name(fates_hydr_organs, fates_string_length) ; + fates_hydro_organ_name:units = "unitless - string" ; + fates_hydro_organ_name:long_name = "Name of plant hydraulics organs (DONT CHANGE, order matches media list in FatesHydraulicsMemMod.F90)" ; + char fates_alloc_organ_name(fates_plant_organs, fates_string_length) ; + fates_alloc_organ_name:units = "unitless - string" ; + fates_alloc_organ_name:long_name = "Name of plant organs (with alloc_organ_id, must match PRTGenericMod.F90)" ; + char fates_landuseclass_name(fates_landuseclass, fates_string_length) ; + fates_landuseclass_name:units = "unitless - string" ; + fates_landuseclass_name:long_name = "Name of the land use classes, for variables associated with dimension fates_landuseclass" ; + char fates_litterclass_name(fates_litterclass, fates_string_length) ; + fates_litterclass_name:units = "unitless - string" ; + fates_litterclass_name:long_name = "Name of the litter classes, for variables associated with dimension fates_litterclass" ; + double fates_alloc_organ_priority(fates_plant_organs, fates_pft) ; + fates_alloc_organ_priority:units = "index" ; + fates_alloc_organ_priority:long_name = "Priority level for allocation, 1: replaces turnover from storage, 2: same priority as storage use/replacement, 3: ascending in order of least importance" ; + double fates_alloc_storage_cushion(fates_pft) ; + fates_alloc_storage_cushion:units = "fraction" ; + fates_alloc_storage_cushion:long_name = "maximum size of storage C pool, relative to maximum size of leaf C pool" ; + double fates_alloc_store_priority_frac(fates_pft) ; + fates_alloc_store_priority_frac:units = "unitless" ; + fates_alloc_store_priority_frac:long_name = "for high-priority organs, the fraction of their turnover demand that is gauranteed to be replaced, and if need-be by storage" ; + double fates_allom_agb1(fates_pft) ; + fates_allom_agb1:units = "variable" ; + fates_allom_agb1:long_name = "Parameter 1 for agb allometry" ; + double fates_allom_agb2(fates_pft) ; + fates_allom_agb2:units = "variable" ; + fates_allom_agb2:long_name = "Parameter 2 for agb allometry" ; + double fates_allom_agb3(fates_pft) ; + fates_allom_agb3:units = "variable" ; + fates_allom_agb3:long_name = "Parameter 3 for agb allometry" ; + double fates_allom_agb4(fates_pft) ; + fates_allom_agb4:units = "variable" ; + fates_allom_agb4:long_name = "Parameter 4 for agb allometry" ; + double fates_allom_agb_frac(fates_pft) ; + fates_allom_agb_frac:units = "fraction" ; + fates_allom_agb_frac:long_name = "Fraction of woody biomass that is above ground" ; + double fates_allom_amode(fates_pft) ; + fates_allom_amode:units = "index" ; + fates_allom_amode:long_name = "AGB allometry function index." ; + double fates_allom_blca_expnt_diff(fates_pft) ; + fates_allom_blca_expnt_diff:units = "unitless" ; + fates_allom_blca_expnt_diff:long_name = "difference between allometric DBH:bleaf and DBH:crown area exponents" ; + double fates_allom_cmode(fates_pft) ; + fates_allom_cmode:units = "index" ; + fates_allom_cmode:long_name = "coarse root biomass allometry function index." ; + double fates_allom_d2bl1(fates_pft) ; + fates_allom_d2bl1:units = "variable" ; + fates_allom_d2bl1:long_name = "Parameter 1 for d2bl allometry" ; + double fates_allom_d2bl2(fates_pft) ; + fates_allom_d2bl2:units = "variable" ; + fates_allom_d2bl2:long_name = "Parameter 2 for d2bl allometry" ; + double fates_allom_d2bl3(fates_pft) ; + fates_allom_d2bl3:units = "unitless" ; + fates_allom_d2bl3:long_name = "Parameter 3 for d2bl allometry" ; + double fates_allom_d2ca_coefficient_max(fates_pft) ; + fates_allom_d2ca_coefficient_max:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_max:long_name = "max (savanna) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2ca_coefficient_min(fates_pft) ; + fates_allom_d2ca_coefficient_min:units = "m2 cm^(-1/beta)" ; + fates_allom_d2ca_coefficient_min:long_name = "min (forest) dbh to area multiplier factor where: area = n*d2ca_coeff*dbh^beta" ; + double fates_allom_d2h1(fates_pft) ; + fates_allom_d2h1:units = "variable" ; + fates_allom_d2h1:long_name = "Parameter 1 for d2h allometry (intercept, or c)" ; + double fates_allom_d2h2(fates_pft) ; + fates_allom_d2h2:units = "variable" ; + fates_allom_d2h2:long_name = "Parameter 2 for d2h allometry (slope, or m)" ; + double fates_allom_d2h3(fates_pft) ; + fates_allom_d2h3:units = "variable" ; + fates_allom_d2h3:long_name = "Parameter 3 for d2h allometry (optional)" ; + double fates_allom_dbh_maxheight(fates_pft) ; + fates_allom_dbh_maxheight:units = "cm" ; + fates_allom_dbh_maxheight:long_name = "the diameter (if any) corresponding to maximum height, diameters may increase beyond this" ; + double fates_allom_dmode(fates_pft) ; + fates_allom_dmode:units = "index" ; + fates_allom_dmode:long_name = "crown depth allometry function index" ; + double fates_allom_fmode(fates_pft) ; + fates_allom_fmode:units = "index" ; + fates_allom_fmode:long_name = "fine root biomass allometry function index." ; + double fates_allom_fnrt_prof_a(fates_pft) ; + fates_allom_fnrt_prof_a:units = "unitless" ; + fates_allom_fnrt_prof_a:long_name = "Fine root profile function, parameter a" ; + double fates_allom_fnrt_prof_b(fates_pft) ; + fates_allom_fnrt_prof_b:units = "unitless" ; + fates_allom_fnrt_prof_b:long_name = "Fine root profile function, parameter b" ; + double fates_allom_fnrt_prof_mode(fates_pft) ; + fates_allom_fnrt_prof_mode:units = "index" ; + fates_allom_fnrt_prof_mode:long_name = "Index to select fine root profile function: 1) Jackson Beta, 2) 1-param exponential 3) 2-param exponential" ; + double fates_allom_frbstor_repro(fates_pft) ; + fates_allom_frbstor_repro:units = "fraction" ; + fates_allom_frbstor_repro:long_name = "fraction of bstore goes to reproduction after plant dies" ; + double fates_allom_h2cd1(fates_pft) ; + fates_allom_h2cd1:units = "variable" ; + fates_allom_h2cd1:long_name = "Parameter 1 for h2cd allometry (exp(log-intercept) or scaling). If allom_dmode=1; this is the same as former crown_depth_frac parameter" ; + double fates_allom_h2cd2(fates_pft) ; + fates_allom_h2cd2:units = "variable" ; + fates_allom_h2cd2:long_name = "Parameter 2 for h2cd allometry (log-slope or exponent). If allom_dmode=1; this is not needed (as exponent is assumed 1)" ; + double fates_allom_hmode(fates_pft) ; + fates_allom_hmode:units = "index" ; + fates_allom_hmode:long_name = "height allometry function index." ; + double fates_allom_l2fr(fates_pft) ; + fates_allom_l2fr:units = "gC/gC" ; + fates_allom_l2fr:long_name = "Allocation parameter: fine root C per leaf C" ; + double fates_allom_la_per_sa_int(fates_pft) ; + fates_allom_la_per_sa_int:units = "m2/cm2" ; + fates_allom_la_per_sa_int:long_name = "Leaf area per sapwood area, intercept" ; + double fates_allom_la_per_sa_slp(fates_pft) ; + fates_allom_la_per_sa_slp:units = "m2/cm2/m" ; + fates_allom_la_per_sa_slp:long_name = "Leaf area per sapwood area rate of change with height, slope (optional)" ; + double fates_allom_lmode(fates_pft) ; + fates_allom_lmode:units = "index" ; + fates_allom_lmode:long_name = "leaf biomass allometry function index." ; + double fates_allom_sai_scaler(fates_pft) ; + fates_allom_sai_scaler:units = "m2/m2" ; + fates_allom_sai_scaler:long_name = "allometric ratio of SAI per LAI" ; + double fates_allom_smode(fates_pft) ; + fates_allom_smode:units = "index" ; + fates_allom_smode:long_name = "sapwood allometry function index." ; + double fates_allom_stmode(fates_pft) ; + fates_allom_stmode:units = "index" ; + fates_allom_stmode:long_name = "storage allometry function index: 1) Storage proportional to leaf biomass (with trimming), 2) Storage proportional to maximum leaf biomass (not trimmed)" ; + double fates_allom_zroot_k(fates_pft) ; + fates_allom_zroot_k:units = "unitless" ; + fates_allom_zroot_k:long_name = "scale coefficient of logistic rooting depth model" ; + double fates_allom_zroot_max_dbh(fates_pft) ; + fates_allom_zroot_max_dbh:units = "cm" ; + fates_allom_zroot_max_dbh:long_name = "dbh at which a plant reaches the maximum value for its maximum rooting depth" ; + double fates_allom_zroot_max_z(fates_pft) ; + fates_allom_zroot_max_z:units = "m" ; + fates_allom_zroot_max_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_max_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_allom_zroot_min_dbh(fates_pft) ; + fates_allom_zroot_min_dbh:units = "cm" ; + fates_allom_zroot_min_dbh:long_name = "dbh at which the maximum rooting depth for a recruit is defined" ; + double fates_allom_zroot_min_z(fates_pft) ; + fates_allom_zroot_min_z:units = "m" ; + fates_allom_zroot_min_z:long_name = "the maximum rooting depth defined at dbh = fates_allom_zroot_min_dbh. note: max_z=min_z=large, sets rooting depth to soil depth" ; + double fates_c2b(fates_pft) ; + fates_c2b:units = "ratio" ; + fates_c2b:long_name = "Carbon to biomass multiplier of bulk structural tissues" ; + double fates_cnp_eca_alpha_ptase(fates_pft) ; + fates_cnp_eca_alpha_ptase:units = "g/m3" ; + fates_cnp_eca_alpha_ptase:long_name = "(INACTIVE, KEEP AT 0) fraction of P from ptase activity sent directly to plant (ECA)" ; + double fates_cnp_eca_decompmicc(fates_pft) ; + fates_cnp_eca_decompmicc:units = "gC/m3" ; + fates_cnp_eca_decompmicc:long_name = "maximum soil microbial decomposer biomass found over depth (will be applied at a reference depth w/ exponential attenuation) (ECA)" ; + double fates_cnp_eca_km_nh4(fates_pft) ; + fates_cnp_eca_km_nh4:units = "gN/m3" ; + fates_cnp_eca_km_nh4:long_name = "half-saturation constant for plant nh4 uptake (ECA)" ; + double fates_cnp_eca_km_no3(fates_pft) ; + fates_cnp_eca_km_no3:units = "gN/m3" ; + fates_cnp_eca_km_no3:long_name = "half-saturation constant for plant no3 uptake (ECA)" ; + double fates_cnp_eca_km_p(fates_pft) ; + fates_cnp_eca_km_p:units = "gP/m3" ; + fates_cnp_eca_km_p:long_name = "half-saturation constant for plant p uptake (ECA)" ; + double fates_cnp_eca_km_ptase(fates_pft) ; + fates_cnp_eca_km_ptase:units = "gP/m3" ; + fates_cnp_eca_km_ptase:long_name = "half-saturation constant for biochemical P (ECA)" ; + double fates_cnp_eca_lambda_ptase(fates_pft) ; + fates_cnp_eca_lambda_ptase:units = "g/m3" ; + fates_cnp_eca_lambda_ptase:long_name = "(INACTIVE, KEEP AT 0) critical value for biochemical production (ECA)" ; + double fates_cnp_eca_vmax_ptase(fates_pft) ; + fates_cnp_eca_vmax_ptase:units = "gP/m2/s" ; + fates_cnp_eca_vmax_ptase:long_name = "maximum production rate for biochemical P (per m2) (ECA)" ; + double fates_cnp_nfix1(fates_pft) ; + fates_cnp_nfix1:units = "fraction" ; + fates_cnp_nfix1:long_name = "fractional surcharge added to maintenance respiration that drives symbiotic fixation" ; + double fates_cnp_nitr_store_ratio(fates_pft) ; + fates_cnp_nitr_store_ratio:units = "(gN/gN)" ; + fates_cnp_nitr_store_ratio:long_name = "storeable (labile) N, as a ratio compared to the N bound in cell structures of other organs (see code)" ; + double fates_cnp_phos_store_ratio(fates_pft) ; + fates_cnp_phos_store_ratio:units = "(gP/gP)" ; + fates_cnp_phos_store_ratio:long_name = "storeable (labile) P, as a ratio compared to the P bound in cell structures of other organs (see code)" ; + double fates_cnp_pid_kd(fates_pft) ; + fates_cnp_pid_kd:units = "unknown" ; + fates_cnp_pid_kd:long_name = "derivative constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_ki(fates_pft) ; + fates_cnp_pid_ki:units = "unknown" ; + fates_cnp_pid_ki:long_name = "integral constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_pid_kp(fates_pft) ; + fates_cnp_pid_kp:units = "unknown" ; + fates_cnp_pid_kp:long_name = "proportional constant of the PID controller on adaptive fine-root biomass" ; + double fates_cnp_prescribed_nuptake(fates_pft) ; + fates_cnp_prescribed_nuptake:units = "fraction" ; + fates_cnp_prescribed_nuptake:long_name = "Prescribed N uptake flux. 0=fully coupled simulation >0=prescribed (experimental)" ; + double fates_cnp_prescribed_puptake(fates_pft) ; + fates_cnp_prescribed_puptake:units = "fraction" ; + fates_cnp_prescribed_puptake:long_name = "Prescribed P uptake flux. 0=fully coupled simulation, >0=prescribed (experimental)" ; + double fates_cnp_store_ovrflw_frac(fates_pft) ; + fates_cnp_store_ovrflw_frac:units = "fraction" ; + fates_cnp_store_ovrflw_frac:long_name = "size of overflow storage (for excess C,N or P) as a fraction of storage target" ; + double fates_cnp_turnover_nitr_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_nitr_retrans:units = "fraction" ; + fates_cnp_turnover_nitr_retrans:long_name = "retranslocation (reabsorbtion) fraction of nitrogen in turnover of scenescing tissues" ; + double fates_cnp_turnover_phos_retrans(fates_plant_organs, fates_pft) ; + fates_cnp_turnover_phos_retrans:units = "fraction" ; + fates_cnp_turnover_phos_retrans:long_name = "retranslocation (reabsorbtion) fraction of phosphorus in turnover of scenescing tissues" ; + double fates_cnp_vmax_nh4(fates_pft) ; + fates_cnp_vmax_nh4:units = "gN/gC/s" ; + fates_cnp_vmax_nh4:long_name = "maximum (potential) uptake rate of NH4 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_nh4 for usage)" ; + double fates_cnp_vmax_no3(fates_pft) ; + fates_cnp_vmax_no3:units = "gN/gC/s" ; + fates_cnp_vmax_no3:long_name = "maximum (potential) uptake rate of NO3 per gC of fineroot biomass (see main/EDPftvarcon.F90 vmax_no3 for usage)" ; + double fates_cnp_vmax_p(fates_pft) ; + fates_cnp_vmax_p:units = "gP/gC/s" ; + fates_cnp_vmax_p:long_name = "maximum production rate for phosphorus (ECA and RD)" ; + double fates_damage_frac(fates_pft) ; + fates_damage_frac:units = "fraction" ; + fates_damage_frac:long_name = "fraction of cohort damaged in each damage event (event frequency specified in the is_it_damage_time subroutine)" ; + double fates_damage_mort_p1(fates_pft) ; + fates_damage_mort_p1:units = "fraction" ; + fates_damage_mort_p1:long_name = "inflection point of damage mortality function, a value of 0.8 means 50% mortality with 80% loss of crown, turn off with a large number" ; + double fates_damage_mort_p2(fates_pft) ; + fates_damage_mort_p2:units = "unitless" ; + fates_damage_mort_p2:long_name = "rate of mortality increase with damage" ; + double fates_damage_recovery_scalar(fates_pft) ; + fates_damage_recovery_scalar:units = "unitless" ; + fates_damage_recovery_scalar:long_name = "fraction of the cohort that recovers from damage" ; + double fates_dev_arbitrary_pft(fates_pft) ; + fates_dev_arbitrary_pft:units = "unknown" ; + fates_dev_arbitrary_pft:long_name = "Unassociated pft dimensioned free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_alpha_SH(fates_pft) ; + fates_fire_alpha_SH:units = "m / (kw/m)**(2/3)" ; + fates_fire_alpha_SH:long_name = "spitfire parameter, alpha scorch height, Equation 16 Thonicke et al 2010" ; + double fates_fire_bark_scaler(fates_pft) ; + fates_fire_bark_scaler:units = "fraction" ; + fates_fire_bark_scaler:long_name = "the thickness of a cohorts bark as a fraction of its dbh" ; + double fates_fire_crown_kill(fates_pft) ; + fates_fire_crown_kill:units = "NA" ; + fates_fire_crown_kill:long_name = "fire parameter, see equation 22 in Thonicke et al 2010" ; + double fates_frag_fnrt_fcel(fates_pft) ; + fates_frag_fnrt_fcel:units = "fraction" ; + fates_frag_fnrt_fcel:long_name = "Fine root litter cellulose fraction" ; + double fates_frag_fnrt_flab(fates_pft) ; + fates_frag_fnrt_flab:units = "fraction" ; + fates_frag_fnrt_flab:long_name = "Fine root litter labile fraction" ; + double fates_frag_fnrt_flig(fates_pft) ; + fates_frag_fnrt_flig:units = "fraction" ; + fates_frag_fnrt_flig:long_name = "Fine root litter lignin fraction" ; + double fates_frag_leaf_fcel(fates_pft) ; + fates_frag_leaf_fcel:units = "fraction" ; + fates_frag_leaf_fcel:long_name = "Leaf litter cellulose fraction" ; + double fates_frag_leaf_flab(fates_pft) ; + fates_frag_leaf_flab:units = "fraction" ; + fates_frag_leaf_flab:long_name = "Leaf litter labile fraction" ; + double fates_frag_leaf_flig(fates_pft) ; + fates_frag_leaf_flig:units = "fraction" ; + fates_frag_leaf_flig:long_name = "Leaf litter lignin fraction" ; + double fates_frag_seed_decay_rate(fates_pft) ; + fates_frag_seed_decay_rate:units = "yr-1" ; + fates_frag_seed_decay_rate:long_name = "fraction of seeds that decay per year" ; + double fates_grperc(fates_pft) ; + fates_grperc:units = "unitless" ; + fates_grperc:long_name = "Growth respiration factor" ; + double fates_hydro_avuln_gs(fates_pft) ; + fates_hydro_avuln_gs:units = "unitless" ; + fates_hydro_avuln_gs:long_name = "shape parameter for stomatal control of water vapor exiting leaf" ; + double fates_hydro_avuln_node(fates_hydr_organs, fates_pft) ; + fates_hydro_avuln_node:units = "unitless" ; + fates_hydro_avuln_node:long_name = "xylem vulnerability curve shape parameter" ; + double fates_hydro_epsil_node(fates_hydr_organs, fates_pft) ; + fates_hydro_epsil_node:units = "MPa" ; + fates_hydro_epsil_node:long_name = "bulk elastic modulus" ; + double fates_hydro_fcap_node(fates_hydr_organs, fates_pft) ; + fates_hydro_fcap_node:units = "unitless" ; + fates_hydro_fcap_node:long_name = "fraction of non-residual water that is capillary in source" ; + double fates_hydro_k_lwp(fates_pft) ; + fates_hydro_k_lwp:units = "unitless" ; + fates_hydro_k_lwp:long_name = "inner leaf humidity scaling coefficient" ; + double fates_hydro_kmax_node(fates_hydr_organs, fates_pft) ; + fates_hydro_kmax_node:units = "kg/MPa/m/s" ; + fates_hydro_kmax_node:long_name = "maximum xylem conductivity per unit conducting xylem area" ; + double fates_hydro_p50_gs(fates_pft) ; + fates_hydro_p50_gs:units = "MPa" ; + fates_hydro_p50_gs:long_name = "water potential at 50% loss of stomatal conductance" ; + double fates_hydro_p50_node(fates_hydr_organs, fates_pft) ; + fates_hydro_p50_node:units = "MPa" ; + fates_hydro_p50_node:long_name = "xylem water potential at 50% loss of conductivity" ; + double fates_hydro_p_taper(fates_pft) ; + fates_hydro_p_taper:units = "unitless" ; + fates_hydro_p_taper:long_name = "xylem taper exponent" ; + double fates_hydro_pinot_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pinot_node:units = "MPa" ; + fates_hydro_pinot_node:long_name = "osmotic potential at full turgor" ; + double fates_hydro_pitlp_node(fates_hydr_organs, fates_pft) ; + fates_hydro_pitlp_node:units = "MPa" ; + fates_hydro_pitlp_node:long_name = "turgor loss point" ; + double fates_hydro_resid_node(fates_hydr_organs, fates_pft) ; + fates_hydro_resid_node:units = "cm3/cm3" ; + fates_hydro_resid_node:long_name = "residual water conent" ; + double fates_hydro_rfrac_stem(fates_pft) ; + fates_hydro_rfrac_stem:units = "fraction" ; + fates_hydro_rfrac_stem:long_name = "fraction of total tree resistance from troot to canopy" ; + double fates_hydro_rs2(fates_pft) ; + fates_hydro_rs2:units = "m" ; + fates_hydro_rs2:long_name = "absorbing root radius" ; + double fates_hydro_srl(fates_pft) ; + fates_hydro_srl:units = "m g-1" ; + fates_hydro_srl:long_name = "specific root length" ; + double fates_hydro_thetas_node(fates_hydr_organs, fates_pft) ; + fates_hydro_thetas_node:units = "cm3/cm3" ; + fates_hydro_thetas_node:long_name = "saturated water content" ; + double fates_hydro_vg_alpha_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_alpha_node:units = "MPa-1" ; + fates_hydro_vg_alpha_node:long_name = "(used if hydr_htftype_node = 2), capillary length parameter in van Genuchten model" ; + double fates_hydro_vg_m_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_m_node:units = "unitless" ; + fates_hydro_vg_m_node:long_name = "(used if hydr_htftype_node = 2),m in van Genuchten 1980 model, 2nd pore size distribution parameter" ; + double fates_hydro_vg_n_node(fates_hydr_organs, fates_pft) ; + fates_hydro_vg_n_node:units = "unitless" ; + fates_hydro_vg_n_node:long_name = "(used if hydr_htftype_node = 2),n in van Genuchten 1980 model, pore size distribution parameter" ; + double fates_landuse_grazing_palatability(fates_pft) ; + fates_landuse_grazing_palatability:units = "unitless 0-1" ; + fates_landuse_grazing_palatability:long_name = "Relative intensity of leaf grazing/browsing per PFT" ; + double fates_landuse_harvest_pprod10(fates_pft) ; + fates_landuse_harvest_pprod10:units = "fraction" ; + fates_landuse_harvest_pprod10:long_name = "fraction of harvest wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_landuse_luc_frac_burned(fates_pft) ; + fates_landuse_luc_frac_burned:units = "fraction" ; + fates_landuse_luc_frac_burned:long_name = "fraction of land use change-generated and not-exported material that is burned (the remainder goes to litter)" ; + double fates_landuse_luc_frac_exported(fates_pft) ; + fates_landuse_luc_frac_exported:units = "fraction" ; + fates_landuse_luc_frac_exported:long_name = "fraction of land use change-generated wood material that is exported to wood product (the remainder is either burned or goes to litter)" ; + double fates_landuse_luc_pprod10(fates_pft) ; + fates_landuse_luc_pprod10:units = "fraction" ; + fates_landuse_luc_pprod10:long_name = "fraction of land use change wood product that goes to 10-year product pool (remainder goes to 100-year pool)" ; + double fates_leaf_agross_btran_model(fates_pft) ; + fates_leaf_agross_btran_model:units = "index" ; + fates_leaf_agross_btran_model:long_name = "model switch for how gross assimilation affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_c3psn(fates_pft) ; + fates_leaf_c3psn:units = "flag" ; + fates_leaf_c3psn:long_name = "Photosynthetic pathway (1=c3, 0=c4)" ; + double fates_leaf_fnps(fates_pft) ; + fates_leaf_fnps:units = "fraction" ; + fates_leaf_fnps:long_name = "fraction of light absorbed by non-photosynthetic pigments" ; + double fates_leaf_jmaxha(fates_pft) ; + fates_leaf_jmaxha:units = "J/mol" ; + fates_leaf_jmaxha:long_name = "activation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxhd(fates_pft) ; + fates_leaf_jmaxhd:units = "J/mol" ; + fates_leaf_jmaxhd:long_name = "deactivation energy for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_jmaxse(fates_pft) ; + fates_leaf_jmaxse:units = "J/mol/K" ; + fates_leaf_jmaxse:long_name = "entropy term for jmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_slamax(fates_pft) ; + fates_leaf_slamax:units = "m^2/gC" ; + fates_leaf_slamax:long_name = "Maximum Specific Leaf Area (SLA), even if under a dense canopy" ; + double fates_leaf_slatop(fates_pft) ; + fates_leaf_slatop:units = "m^2/gC" ; + fates_leaf_slatop:long_name = "Specific Leaf Area (SLA) at top of canopy, projected area basis" ; + double fates_leaf_stomatal_btran_model(fates_pft) ; + fates_leaf_stomatal_btran_model:units = "index" ; + fates_leaf_stomatal_btran_model:long_name = "model switch for how btran affects conductance. See LeafBiophysicsMod.F90, integer constants: btran_on_" ; + double fates_leaf_stomatal_intercept(fates_pft) ; + fates_leaf_stomatal_intercept:units = "umol H2O/m**2/s" ; + fates_leaf_stomatal_intercept:long_name = "Minimum unstressed stomatal conductance for Ball-Berry model and Medlyn model" ; + double fates_leaf_stomatal_slope_ballberry(fates_pft) ; + fates_leaf_stomatal_slope_ballberry:units = "unitless" ; + fates_leaf_stomatal_slope_ballberry:long_name = "stomatal slope parameter, as per Ball-Berry" ; + double fates_leaf_stomatal_slope_medlyn(fates_pft) ; + fates_leaf_stomatal_slope_medlyn:units = "KPa**0.5" ; + fates_leaf_stomatal_slope_medlyn:long_name = "stomatal slope parameter, as per Medlyn" ; + double fates_leaf_vcmax25top(fates_leafage_class, fates_pft) ; + fates_leaf_vcmax25top:units = "umol CO2/m^2/s" ; + fates_leaf_vcmax25top:long_name = "maximum carboxylation rate of Rub. at 25C, canopy top" ; + double fates_leaf_vcmaxha(fates_pft) ; + fates_leaf_vcmaxha:units = "J/mol" ; + fates_leaf_vcmaxha:long_name = "activation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxhd(fates_pft) ; + fates_leaf_vcmaxhd:units = "J/mol" ; + fates_leaf_vcmaxhd:long_name = "deactivation energy for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leaf_vcmaxse(fates_pft) ; + fates_leaf_vcmaxse:units = "J/mol/K" ; + fates_leaf_vcmaxse:long_name = "entropy term for vcmax. NOTE: if fates_leaf_photo_tempsens_model=2 then these values are NOT USED" ; + double fates_leafn_vert_scaler_coeff1(fates_pft) ; + fates_leafn_vert_scaler_coeff1:units = "unitless" ; + fates_leafn_vert_scaler_coeff1:long_name = "Coefficient one for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_leafn_vert_scaler_coeff2(fates_pft) ; + fates_leafn_vert_scaler_coeff2:units = "unitless" ; + fates_leafn_vert_scaler_coeff2:long_name = "Coefficient two for decrease in leaf nitrogen through the canopy, from Lloyd et al. 2010." ; + double fates_maintresp_leaf_atkin2017_baserate(fates_pft) ; + fates_maintresp_leaf_atkin2017_baserate:units = "umol CO2/m^2/s" ; + fates_maintresp_leaf_atkin2017_baserate:long_name = "Leaf maintenance respiration base rate parameter (r0) per Atkin et al 2017" ; + double fates_maintresp_leaf_ryan1991_baserate(fates_pft) ; + fates_maintresp_leaf_ryan1991_baserate:units = "gC/gN/s" ; + fates_maintresp_leaf_ryan1991_baserate:long_name = "Leaf maintenance respiration base rate per Ryan et al 1991" ; + double fates_maintresp_leaf_vert_scaler_coeff1(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff1:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff1:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff1." ; + double fates_maintresp_leaf_vert_scaler_coeff2(fates_pft) ; + fates_maintresp_leaf_vert_scaler_coeff2:units = "unitless" ; + fates_maintresp_leaf_vert_scaler_coeff2:long_name = "Leaf maintenance respiration decrease through the canopy. Only applies to Atkin et al. 2017. For proportionality between photosynthesis and respiration through the canopy, match with fates_leafn_vert_scaler_coeff2." ; + double fates_maintresp_reduction_curvature(fates_pft) ; + fates_maintresp_reduction_curvature:units = "unitless (0-1)" ; + fates_maintresp_reduction_curvature:long_name = "curvature of MR reduction as f(carbon storage), 1=linear, 0=very curved" ; + double fates_maintresp_reduction_intercept(fates_pft) ; + fates_maintresp_reduction_intercept:units = "unitless (0-1)" ; + fates_maintresp_reduction_intercept:long_name = "intercept of MR reduction as f(carbon storage), 0=no throttling, 1=max throttling" ; + double fates_maintresp_reduction_upthresh(fates_pft) ; + fates_maintresp_reduction_upthresh:units = "unitless (0-1)" ; + fates_maintresp_reduction_upthresh:long_name = "upper threshold for storage biomass (relative to leaf biomass) above which MR is not reduced" ; + double fates_mort_bmort(fates_pft) ; + fates_mort_bmort:units = "1/yr" ; + fates_mort_bmort:long_name = "background mortality rate" ; + 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_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. " ; + double fates_mort_ip_size_senescence(fates_pft) ; + fates_mort_ip_size_senescence:units = "dbh cm" ; + fates_mort_ip_size_senescence:long_name = "Mortality dbh senescence inflection point. If _ this mortality term is off. Setting this value turns on size dependent mortality" ; + double fates_mort_prescribed_canopy(fates_pft) ; + fates_mort_prescribed_canopy:units = "1/yr" ; + fates_mort_prescribed_canopy:long_name = "mortality rate of canopy trees for prescribed physiology mode" ; + double fates_mort_prescribed_understory(fates_pft) ; + fates_mort_prescribed_understory:units = "1/yr" ; + fates_mort_prescribed_understory:long_name = "mortality rate of understory trees for prescribed physiology mode" ; + double fates_mort_r_age_senescence(fates_pft) ; + fates_mort_r_age_senescence:units = "mortality rate year^-1" ; + fates_mort_r_age_senescence:long_name = "Mortality age senescence rate of change. Sensible range is around 0.03-0.06. Larger values givesteeper mortality curves." ; + double fates_mort_r_size_senescence(fates_pft) ; + fates_mort_r_size_senescence:units = "mortality rate dbh^-1" ; + fates_mort_r_size_senescence:long_name = "Mortality dbh senescence rate of change. Sensible range is around 0.03-0.06. Larger values give steeper mortality curves." ; + double fates_mort_scalar_coldstress(fates_pft) ; + fates_mort_scalar_coldstress:units = "1/yr" ; + fates_mort_scalar_coldstress:long_name = "maximum mortality rate from cold stress" ; + double fates_mort_scalar_cstarvation(fates_pft) ; + fates_mort_scalar_cstarvation:units = "1/yr" ; + fates_mort_scalar_cstarvation:long_name = "maximum mortality rate from carbon starvation" ; + double fates_mort_scalar_hydrfailure(fates_pft) ; + fates_mort_scalar_hydrfailure:units = "1/yr" ; + fates_mort_scalar_hydrfailure:long_name = "maximum mortality rate from hydraulic failure" ; + double fates_mort_upthresh_cstarvation(fates_pft) ; + fates_mort_upthresh_cstarvation:units = "unitless" ; + fates_mort_upthresh_cstarvation:long_name = "threshold for storage biomass (relative to target leaf biomass) above which carbon starvation is zero" ; + double fates_nonhydro_smpsc(fates_pft) ; + fates_nonhydro_smpsc:units = "mm" ; + fates_nonhydro_smpsc:long_name = "Soil water potential at full stomatal closure" ; + double fates_nonhydro_smpso(fates_pft) ; + fates_nonhydro_smpso:units = "mm" ; + fates_nonhydro_smpso:long_name = "Soil water potential at full stomatal opening" ; + double fates_phen_cold_size_threshold(fates_pft) ; + fates_phen_cold_size_threshold:units = "cm" ; + fates_phen_cold_size_threshold:long_name = "the dbh size above which will lead to phenology-related stem and leaf drop" ; + double fates_phen_drought_threshold(fates_pft) ; + fates_phen_drought_threshold:units = "m3/m3 or mm" ; + fates_phen_drought_threshold:long_name = "threshold for drought phenology (or lower threshold for semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_flush_fraction(fates_pft) ; + fates_phen_flush_fraction:units = "fraction" ; + fates_phen_flush_fraction:long_name = "Upon bud-burst, the maximum fraction of storage carbon used for flushing leaves" ; + double fates_phen_fnrt_drop_fraction(fates_pft) ; + fates_phen_fnrt_drop_fraction:units = "fraction" ; + fates_phen_fnrt_drop_fraction:long_name = "fraction of fine roots to drop during drought/cold" ; + double fates_phen_leaf_habit(fates_pft) ; + fates_phen_leaf_habit:units = "flag" ; + fates_phen_leaf_habit:long_name = "Flag for leaf phenology habit. 1 - evergreen; 2 - season (cold) deciduous; 3 - stress (hydro) deciduous; 4 - stress (hydro) semi-deciduous" ; + double fates_phen_mindaysoff(fates_pft) ; + fates_phen_mindaysoff:units = "days" ; + fates_phen_mindaysoff:long_name = "day threshold compared against days since leaves abscised (shed)" ; + double fates_phen_moist_threshold(fates_pft) ; + fates_phen_moist_threshold:units = "m3/m3 or mm" ; + fates_phen_moist_threshold:long_name = "upper threshold for drought phenology (only for drought semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; + double fates_phen_stem_drop_fraction(fates_pft) ; + fates_phen_stem_drop_fraction:units = "fraction" ; + fates_phen_stem_drop_fraction:long_name = "fraction of stems to drop for non-woody species during drought/cold" ; + double fates_prescribed_npp_canopy(fates_pft) ; + fates_prescribed_npp_canopy:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_canopy:long_name = "NPP per unit crown area of canopy trees for prescribed physiology mode" ; + double fates_prescribed_npp_understory(fates_pft) ; + fates_prescribed_npp_understory:units = "kgC / m^2 / yr" ; + fates_prescribed_npp_understory:long_name = "NPP per unit crown area of understory trees for prescribed physiology mode" ; + double fates_rad_leaf_clumping_index(fates_pft) ; + fates_rad_leaf_clumping_index:units = "fraction (0-1)" ; + fates_rad_leaf_clumping_index:long_name = "factor describing how much self-occlusion of leaf scattering elements decreases light interception" ; + double fates_rad_leaf_rhonir(fates_pft) ; + fates_rad_leaf_rhonir:units = "fraction" ; + fates_rad_leaf_rhonir:long_name = "Leaf reflectance: near-IR" ; + double fates_rad_leaf_rhovis(fates_pft) ; + fates_rad_leaf_rhovis:units = "fraction" ; + fates_rad_leaf_rhovis:long_name = "Leaf reflectance: visible" ; + double fates_rad_leaf_taunir(fates_pft) ; + fates_rad_leaf_taunir:units = "fraction" ; + fates_rad_leaf_taunir:long_name = "Leaf transmittance: near-IR" ; + double fates_rad_leaf_tauvis(fates_pft) ; + fates_rad_leaf_tauvis:units = "fraction" ; + fates_rad_leaf_tauvis:long_name = "Leaf transmittance: visible" ; + double fates_rad_leaf_xl(fates_pft) ; + fates_rad_leaf_xl:units = "unitless" ; + fates_rad_leaf_xl:long_name = "Leaf/stem orientation index" ; + double fates_rad_stem_rhonir(fates_pft) ; + fates_rad_stem_rhonir:units = "fraction" ; + fates_rad_stem_rhonir:long_name = "Stem reflectance: near-IR" ; + double fates_rad_stem_rhovis(fates_pft) ; + fates_rad_stem_rhovis:units = "fraction" ; + fates_rad_stem_rhovis:long_name = "Stem reflectance: visible" ; + double fates_rad_stem_taunir(fates_pft) ; + fates_rad_stem_taunir:units = "fraction" ; + fates_rad_stem_taunir:long_name = "Stem transmittance: near-IR" ; + double fates_rad_stem_tauvis(fates_pft) ; + fates_rad_stem_tauvis:units = "fraction" ; + fates_rad_stem_tauvis:long_name = "Stem transmittance: visible" ; + double fates_recruit_height_min(fates_pft) ; + fates_recruit_height_min:units = "m" ; + fates_recruit_height_min:long_name = "the minimum height (ie starting height) of a newly recruited plant" ; + double fates_recruit_init_density(fates_pft) ; + fates_recruit_init_density:units = "stems/m2" ; + fates_recruit_init_density:long_name = "initial seedling density for a cold-start near-bare-ground simulation. If negative sets initial tree dbh - only to be used in nocomp mode" ; + double fates_recruit_prescribed_rate(fates_pft) ; + fates_recruit_prescribed_rate:units = "n/yr" ; + fates_recruit_prescribed_rate:long_name = "recruitment rate for prescribed physiology mode" ; + double fates_recruit_seed_alloc(fates_pft) ; + fates_recruit_seed_alloc:units = "fraction" ; + fates_recruit_seed_alloc:long_name = "fraction of available carbon balance allocated to seeds" ; + double fates_recruit_seed_alloc_mature(fates_pft) ; + fates_recruit_seed_alloc_mature:units = "fraction" ; + fates_recruit_seed_alloc_mature:long_name = "fraction of available carbon balance allocated to seeds in mature plants (adds to fates_seed_alloc)" ; + double fates_recruit_seed_dbh_repro_threshold(fates_pft) ; + fates_recruit_seed_dbh_repro_threshold:units = "cm" ; + fates_recruit_seed_dbh_repro_threshold:long_name = "the diameter where the plant will increase allocation to the seed pool by fraction: fates_recruit_seed_alloc_mature" ; + double fates_recruit_seed_germination_rate(fates_pft) ; + fates_recruit_seed_germination_rate:units = "yr-1" ; + fates_recruit_seed_germination_rate:long_name = "fraction of seeds that germinate per year" ; + double fates_recruit_seed_supplement(fates_pft) ; + fates_recruit_seed_supplement:units = "KgC/m2/yr" ; + fates_recruit_seed_supplement:long_name = "Supplemental external seed rain source term (non-mass conserving)" ; + double fates_seed_dispersal_fraction(fates_pft) ; + fates_seed_dispersal_fraction:units = "fraction" ; + fates_seed_dispersal_fraction:long_name = "fraction of seed rain to be dispersed to other grid cells" ; + double fates_seed_dispersal_max_dist(fates_pft) ; + fates_seed_dispersal_max_dist:units = "m" ; + fates_seed_dispersal_max_dist:long_name = "maximum seed dispersal distance for a given pft" ; + double fates_seed_dispersal_pdf_scale(fates_pft) ; + fates_seed_dispersal_pdf_scale:units = "unitless" ; + fates_seed_dispersal_pdf_scale:long_name = "seed dispersal probability density function scale parameter, A, Table 1 Bullock et al 2016" ; + double fates_seed_dispersal_pdf_shape(fates_pft) ; + fates_seed_dispersal_pdf_shape:units = "unitless" ; + fates_seed_dispersal_pdf_shape:long_name = "seed dispersal probability density function shape parameter, B, Table 1 Bullock et al 2016" ; + double fates_stoich_nitr(fates_plant_organs, fates_pft) ; + fates_stoich_nitr:units = "gN/gC" ; + fates_stoich_nitr:long_name = "target nitrogen concentration (ratio with carbon) of organs" ; + double fates_stoich_phos(fates_plant_organs, fates_pft) ; + fates_stoich_phos:units = "gP/gC" ; + fates_stoich_phos:long_name = "target phosphorus concentration (ratio with carbon) of organs" ; + double fates_trim_inc(fates_pft) ; + fates_trim_inc:units = "m2/m2" ; + fates_trim_inc:long_name = "Arbitrary incremental change in trimming function." ; + double fates_trim_limit(fates_pft) ; + fates_trim_limit:units = "m2/m2" ; + fates_trim_limit:long_name = "Arbitrary limit to reductions in leaf area with stress" ; + double fates_trs_repro_alloc_a(fates_pft) ; + fates_trs_repro_alloc_a:units = "fraction" ; + fates_trs_repro_alloc_a:long_name = "shape parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_alloc_b(fates_pft) ; + fates_trs_repro_alloc_b:units = "fraction" ; + fates_trs_repro_alloc_b:long_name = "intercept parameter for sigmoidal function relating dbh to reproductive allocation" ; + double fates_trs_repro_frac_seed(fates_pft) ; + fates_trs_repro_frac_seed:units = "fraction" ; + fates_trs_repro_frac_seed:long_name = "fraction of reproductive mass that is seed" ; + double fates_trs_seedling_a_emerg(fates_pft) ; + fates_trs_seedling_a_emerg:units = "day -1" ; + fates_trs_seedling_a_emerg:long_name = "mean fraction of seed bank emerging" ; + double fates_trs_seedling_b_emerg(fates_pft) ; + fates_trs_seedling_b_emerg:units = "day -1" ; + fates_trs_seedling_b_emerg:long_name = "seedling emergence sensitivity to soil moisture" ; + double fates_trs_seedling_background_mort(fates_pft) ; + fates_trs_seedling_background_mort:units = "yr-1" ; + fates_trs_seedling_background_mort:long_name = "background seedling mortality rate" ; + double fates_trs_seedling_h2o_mort_a(fates_pft) ; + fates_trs_seedling_h2o_mort_a:units = "-" ; + fates_trs_seedling_h2o_mort_a:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_b(fates_pft) ; + fates_trs_seedling_h2o_mort_b:units = "-" ; + fates_trs_seedling_h2o_mort_b:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_h2o_mort_c(fates_pft) ; + fates_trs_seedling_h2o_mort_c:units = "-" ; + fates_trs_seedling_h2o_mort_c:long_name = "coefficient in moisture-based seedling mortality" ; + double fates_trs_seedling_light_mort_a(fates_pft) ; + fates_trs_seedling_light_mort_a:units = "-" ; + fates_trs_seedling_light_mort_a:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_mort_b(fates_pft) ; + fates_trs_seedling_light_mort_b:units = "-" ; + fates_trs_seedling_light_mort_b:long_name = "light-based seedling mortality coefficient" ; + double fates_trs_seedling_light_rec_a(fates_pft) ; + fates_trs_seedling_light_rec_a:units = "-" ; + fates_trs_seedling_light_rec_a:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_light_rec_b(fates_pft) ; + fates_trs_seedling_light_rec_b:units = "-" ; + fates_trs_seedling_light_rec_b:long_name = "coefficient in light-based seedling to sapling transition" ; + double fates_trs_seedling_mdd_crit(fates_pft) ; + fates_trs_seedling_mdd_crit:units = "mm H2O day" ; + fates_trs_seedling_mdd_crit:long_name = "critical moisture deficit (suction) day accumulation for seedling moisture-based seedling mortality to begin" ; + double fates_trs_seedling_par_crit_germ(fates_pft) ; + fates_trs_seedling_par_crit_germ:units = "MJ m-2 day-1" ; + fates_trs_seedling_par_crit_germ:long_name = "critical light level for germination" ; + double fates_trs_seedling_psi_crit(fates_pft) ; + fates_trs_seedling_psi_crit:units = "mm H2O" ; + fates_trs_seedling_psi_crit:long_name = "critical soil moisture (suction) for seedling stress" ; + double fates_trs_seedling_psi_emerg(fates_pft) ; + fates_trs_seedling_psi_emerg:units = "mm h20 suction" ; + fates_trs_seedling_psi_emerg:long_name = "critical soil moisture for seedling emergence" ; + double fates_trs_seedling_root_depth(fates_pft) ; + fates_trs_seedling_root_depth:units = "m" ; + fates_trs_seedling_root_depth:long_name = "rooting depth of seedlings" ; + double fates_turb_displar(fates_pft) ; + fates_turb_displar:units = "unitless" ; + fates_turb_displar:long_name = "Ratio of displacement height to canopy top height" ; + double fates_turb_leaf_diameter(fates_pft) ; + fates_turb_leaf_diameter:units = "m" ; + fates_turb_leaf_diameter:long_name = "Characteristic leaf dimension" ; + double fates_turb_z0mr(fates_pft) ; + fates_turb_z0mr:units = "unitless" ; + fates_turb_z0mr:long_name = "Ratio of momentum roughness length to canopy top height" ; + double fates_turnover_branch(fates_pft) ; + fates_turnover_branch:units = "yr" ; + fates_turnover_branch:long_name = "turnover time of branches" ; + double fates_turnover_fnrt(fates_pft) ; + fates_turnover_fnrt:units = "yr" ; + fates_turnover_fnrt:long_name = "root longevity (alternatively, turnover time)" ; + double fates_turnover_leaf_canopy(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_canopy:units = "yr" ; + fates_turnover_leaf_canopy:long_name = "Leaf longevity (ie turnover timescale) of canopy plants. For drought-deciduous PFTs, this also indicates the maximum length of the growing (i.e., leaves on) season." ; + double fates_turnover_leaf_ustory(fates_leafage_class, fates_pft) ; + fates_turnover_leaf_ustory:units = "yr" ; + fates_turnover_leaf_ustory:long_name = "Leaf longevity (ie turnover timescale) of understory plants." ; + double fates_turnover_senleaf_fdrought(fates_pft) ; + fates_turnover_senleaf_fdrought:units = "unitless[0-1]" ; + fates_turnover_senleaf_fdrought:long_name = "multiplication factor for leaf longevity of senescent leaves during drought" ; + double fates_wood_density(fates_pft) ; + fates_wood_density:units = "g/cm3" ; + fates_wood_density:long_name = "mean density of woody tissue in plant" ; + double fates_woody(fates_pft) ; + fates_woody:units = "logical flag" ; + fates_woody:long_name = "Binary woody lifeform flag" ; + double fates_hlm_pft_map(fates_hlm_pftno, fates_pft) ; + fates_hlm_pft_map:units = "area fraction" ; + fates_hlm_pft_map:long_name = "In fixed biogeog mode, fraction of HLM area associated with each FATES PFT" ; + double fates_fire_FBD(fates_litterclass) ; + fates_fire_FBD:units = "kg Biomass/m3" ; + fates_fire_FBD:long_name = "fuel bulk density" ; + double fates_fire_low_moisture_Coeff(fates_litterclass) ; + fates_fire_low_moisture_Coeff:units = "NA" ; + fates_fire_low_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_low_moisture_Slope(fates_litterclass) ; + fates_fire_low_moisture_Slope:units = "NA" ; + fates_fire_low_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture(fates_litterclass) ; + fates_fire_mid_moisture:units = "NA" ; + fates_fire_mid_moisture:long_name = "spitfire litter moisture threshold to be considered medium dry" ; + double fates_fire_mid_moisture_Coeff(fates_litterclass) ; + fates_fire_mid_moisture_Coeff:units = "NA" ; + fates_fire_mid_moisture_Coeff:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_mid_moisture_Slope(fates_litterclass) ; + fates_fire_mid_moisture_Slope:units = "NA" ; + fates_fire_mid_moisture_Slope:long_name = "spitfire parameter, equation B1 Thonicke et al 2010" ; + double fates_fire_min_moisture(fates_litterclass) ; + fates_fire_min_moisture:units = "NA" ; + fates_fire_min_moisture:long_name = "spitfire litter moisture threshold to be considered very dry" ; + double fates_fire_SAV(fates_litterclass) ; + fates_fire_SAV:units = "cm-1" ; + fates_fire_SAV:long_name = "fuel surface area to volume ratio" ; + double fates_frag_maxdecomp(fates_litterclass) ; + fates_frag_maxdecomp:units = "yr-1" ; + fates_frag_maxdecomp:long_name = "maximum rate of litter & CWD transfer from non-decomposing class into decomposing class" ; + double fates_frag_cwd_frac(fates_NCWD) ; + fates_frag_cwd_frac:units = "fraction" ; + fates_frag_cwd_frac:long_name = "fraction of woody (bdead+bsw) biomass destined for CWD pool" ; + double fates_landuse_crop_lu_pft_vector(fates_landuseclass) ; + fates_landuse_crop_lu_pft_vector:units = "NA" ; + fates_landuse_crop_lu_pft_vector:long_name = "the FATES PFT index to use on a given crop land-use type (dummy value of -999 for non-crop types)" ; + double fates_landuse_grazing_rate(fates_landuseclass) ; + fates_landuse_grazing_rate:units = "1/day" ; + fates_landuse_grazing_rate:long_name = "fraction of leaf biomass consumed by grazers per day" ; + double fates_max_nocomp_pfts_by_landuse(fates_landuseclass) ; + fates_max_nocomp_pfts_by_landuse:units = "count" ; + fates_max_nocomp_pfts_by_landuse:long_name = "maximum number of nocomp PFTs on each land use type (only used in nocomp mode)" ; + double fates_maxpatches_by_landuse(fates_landuseclass) ; + fates_maxpatches_by_landuse:units = "count" ; + fates_maxpatches_by_landuse:long_name = "maximum number of patches per site on each land use type" ; + double fates_canopy_closure_thresh ; + fates_canopy_closure_thresh:units = "unitless" ; + fates_canopy_closure_thresh:long_name = "tree canopy coverage at which crown area allometry changes from savanna to forest value" ; + double fates_cnp_eca_plant_escalar ; + fates_cnp_eca_plant_escalar:units = "" ; + fates_cnp_eca_plant_escalar:long_name = "scaling factor for plant fine root biomass to calculate nutrient carrier enzyme abundance (ECA)" ; + double fates_cohort_age_fusion_tol ; + fates_cohort_age_fusion_tol:units = "unitless" ; + fates_cohort_age_fusion_tol:long_name = "minimum fraction in differece in cohort age between cohorts." ; + double fates_cohort_size_fusion_tol ; + fates_cohort_size_fusion_tol:units = "unitless" ; + fates_cohort_size_fusion_tol:long_name = "minimum fraction in difference in dbh between cohorts" ; + double fates_comp_excln ; + fates_comp_excln:units = "none" ; + fates_comp_excln:long_name = "IF POSITIVE: weighting factor (exponent on dbh) for canopy layer exclusion and promotion, IF NEGATIVE: switch to use deterministic height sorting" ; + double fates_damage_canopy_layer_code ; + fates_damage_canopy_layer_code:units = "unitless" ; + fates_damage_canopy_layer_code:long_name = "Integer code that decides whether damage affects canopy trees (1), understory trees (2)" ; + double fates_damage_event_code ; + fates_damage_event_code:units = "unitless" ; + fates_damage_event_code:long_name = "Integer code that options how damage events are structured" ; + double fates_dev_arbitrary ; + fates_dev_arbitrary:units = "unknown" ; + fates_dev_arbitrary:long_name = "Unassociated free parameter that developers can use for testing arbitrary new hypotheses" ; + double fates_fire_active_crown_fire ; + fates_fire_active_crown_fire:units = "0 or 1" ; + fates_fire_active_crown_fire:long_name = "flag, 1=active crown fire 0=no active crown fire" ; + double fates_fire_cg_strikes ; + fates_fire_cg_strikes:units = "fraction (0-1)" ; + fates_fire_cg_strikes:long_name = "fraction of cloud to ground lightning strikes" ; + double fates_fire_drying_ratio ; + fates_fire_drying_ratio:units = "NA" ; + fates_fire_drying_ratio:long_name = "spitfire parameter, fire drying ratio for fuel moisture, alpha_FMC EQ 6 Thonicke et al 2010" ; + double fates_fire_durat_slope ; + fates_fire_durat_slope:units = "NA" ; + fates_fire_durat_slope:long_name = "spitfire parameter, fire max duration slope, Equation 14 Thonicke et al 2010" ; + double fates_fire_fdi_alpha ; + fates_fire_fdi_alpha:units = "NA" ; + fates_fire_fdi_alpha:long_name = "spitfire parameter, EQ 7 Venevsky et al. GCB 2002,(modified EQ 8 Thonicke et al. 2010) " ; + double fates_fire_fuel_energy ; + fates_fire_fuel_energy:units = "kJ/kg" ; + fates_fire_fuel_energy:long_name = "spitfire parameter, heat content of fuel" ; + double fates_fire_max_durat ; + fates_fire_max_durat:units = "minutes" ; + fates_fire_max_durat:long_name = "spitfire parameter, fire maximum duration, Equation 14 Thonicke et al 2010" ; + double fates_fire_miner_damp ; + fates_fire_miner_damp:units = "NA" ; + fates_fire_miner_damp:long_name = "spitfire parameter, mineral-dampening coefficient EQ A1 Thonicke et al 2010 " ; + double fates_fire_miner_total ; + fates_fire_miner_total:units = "fraction" ; + fates_fire_miner_total:long_name = "spitfire parameter, total mineral content, Table A1 Thonicke et al 2010" ; + double fates_fire_nignitions ; + fates_fire_nignitions:units = "ignitions per year per km2" ; + fates_fire_nignitions:long_name = "number of annual ignitions per square km" ; + double fates_fire_part_dens ; + fates_fire_part_dens:units = "kg/m2" ; + fates_fire_part_dens:long_name = "spitfire parameter, oven dry particle density, Table A1 Thonicke et al 2010" ; + double fates_fire_threshold ; + fates_fire_threshold:units = "kW/m" ; + fates_fire_threshold:long_name = "spitfire parameter, fire intensity threshold for tracking fires that spread" ; + double fates_frag_cwd_fcel ; + fates_frag_cwd_fcel:units = "unitless" ; + fates_frag_cwd_fcel:long_name = "Cellulose fraction for CWD" ; + double fates_frag_cwd_flig ; + fates_frag_cwd_flig:units = "unitless" ; + fates_frag_cwd_flig:long_name = "Lignin fraction of coarse woody debris" ; + double fates_hydro_kmax_rsurf1 ; + fates_hydro_kmax_rsurf1:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf1:long_name = "maximum conducitivity for unit root surface (into root)" ; + double fates_hydro_kmax_rsurf2 ; + fates_hydro_kmax_rsurf2:units = "kg water/m2 root area/Mpa/s" ; + fates_hydro_kmax_rsurf2:long_name = "maximum conducitivity for unit root surface (out of root)" ; + double fates_hydro_psi0 ; + fates_hydro_psi0:units = "MPa" ; + fates_hydro_psi0:long_name = "sapwood water potential at saturation" ; + double fates_hydro_psicap ; + fates_hydro_psicap:units = "MPa" ; + fates_hydro_psicap:long_name = "sapwood water potential at which capillary reserves exhausted" ; + double fates_landuse_grazing_carbon_use_eff ; + fates_landuse_grazing_carbon_use_eff:units = "unitless" ; + fates_landuse_grazing_carbon_use_eff:long_name = "carbon use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_maxheight ; + fates_landuse_grazing_maxheight:units = "m" ; + fates_landuse_grazing_maxheight:long_name = "maximum height that grazers (browsers, actually) can reach" ; + double fates_landuse_grazing_nitrogen_use_eff ; + fates_landuse_grazing_nitrogen_use_eff:units = "unitless" ; + fates_landuse_grazing_nitrogen_use_eff:long_name = "nitrogen use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_grazing_phosphorus_use_eff ; + fates_landuse_grazing_phosphorus_use_eff:units = "unitless" ; + fates_landuse_grazing_phosphorus_use_eff:long_name = "phosphorus use efficiency of material eaten by grazers/browsers (i.e. amount in manure / amount consumed)" ; + double fates_landuse_logging_coll_under_frac ; + fates_landuse_logging_coll_under_frac:units = "fraction" ; + fates_landuse_logging_coll_under_frac:long_name = "Fraction of stems killed in the understory when logging generates disturbance" ; + double fates_landuse_logging_collateral_frac ; + fates_landuse_logging_collateral_frac:units = "fraction" ; + fates_landuse_logging_collateral_frac:long_name = "Fraction of large stems in upperstory that die from logging collateral damage" ; + double fates_landuse_logging_dbhmax ; + fates_landuse_logging_dbhmax:units = "cm" ; + fates_landuse_logging_dbhmax:long_name = "Maximum dbh below which logging is applied (unset values flag this to be unused)" ; + double fates_landuse_logging_dbhmax_infra ; + fates_landuse_logging_dbhmax_infra:units = "cm" ; + fates_landuse_logging_dbhmax_infra:long_name = "Tree diameter, above which infrastructure from logging does not impact damage or mortality." ; + double fates_landuse_logging_dbhmin ; + fates_landuse_logging_dbhmin:units = "cm" ; + fates_landuse_logging_dbhmin:long_name = "Minimum dbh at which logging is applied" ; + double fates_landuse_logging_direct_frac ; + fates_landuse_logging_direct_frac:units = "fraction" ; + fates_landuse_logging_direct_frac:long_name = "Fraction of stems logged directly per event" ; + double fates_landuse_logging_event_code ; + fates_landuse_logging_event_code:units = "unitless" ; + fates_landuse_logging_event_code:long_name = "Integer code that options how logging events are structured" ; + double fates_landuse_logging_export_frac ; + fates_landuse_logging_export_frac:units = "fraction" ; + fates_landuse_logging_export_frac:long_name = "fraction of trunk product being shipped offsite, the leftovers will be left onsite as large CWD" ; + double fates_landuse_logging_mechanical_frac ; + fates_landuse_logging_mechanical_frac:units = "fraction" ; + fates_landuse_logging_mechanical_frac:long_name = "Fraction of stems killed due infrastructure an other mechanical means" ; + double fates_leaf_photo_temp_acclim_thome_time ; + fates_leaf_photo_temp_acclim_thome_time:units = "years" ; + fates_leaf_photo_temp_acclim_thome_time:long_name = "Length of the window for the long-term (i.e. T_home in Kumarathunge et al 2019) exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_photo_temp_acclim_timescale ; + fates_leaf_photo_temp_acclim_timescale:units = "days" ; + fates_leaf_photo_temp_acclim_timescale:long_name = "Length of the window for the exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_maintresp_leaf_model=2 or fates_leaf_photo_tempsens_model = 2)" ; + double fates_leaf_theta_cj_c3 ; + fates_leaf_theta_cj_c3:units = "unitless" ; + fates_leaf_theta_cj_c3:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_leaf_theta_cj_c4 ; + fates_leaf_theta_cj_c4:units = "unitless" ; + fates_leaf_theta_cj_c4:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; + double fates_maintresp_nonleaf_baserate ; + fates_maintresp_nonleaf_baserate:units = "gC/gN/s" ; + fates_maintresp_nonleaf_baserate:long_name = "Base maintenance respiration rate for plant tissues, using Ryan 1991" ; + double fates_maxcohort ; + fates_maxcohort:units = "count" ; + fates_maxcohort:long_name = "maximum number of cohorts per patch. Actual number of cohorts also depend on cohort fusion tolerances" ; + double fates_mort_disturb_frac ; + fates_mort_disturb_frac:units = "fraction" ; + fates_mort_disturb_frac:long_name = "fraction of canopy mortality that results in disturbance (i.e. transfer of area from old to new patch)" ; + double fates_mort_understorey_death ; + fates_mort_understorey_death:units = "fraction" ; + fates_mort_understorey_death:long_name = "fraction of plants in understorey cohort impacted by overstorey tree-fall" ; + double fates_patch_fusion_tol ; + fates_patch_fusion_tol:units = "unitless" ; + fates_patch_fusion_tol:long_name = "minimum fraction in difference in profiles between patches" ; + double fates_phen_chilltemp ; + fates_phen_chilltemp:units = "degrees C" ; + fates_phen_chilltemp:long_name = "chilling day counting threshold for vegetation" ; + double fates_phen_coldtemp ; + fates_phen_coldtemp:units = "degrees C" ; + fates_phen_coldtemp:long_name = "vegetation temperature exceedance that flags a cold-day for leaf-drop" ; + double fates_phen_gddthresh_a ; + fates_phen_gddthresh_a:units = "none" ; + fates_phen_gddthresh_a:long_name = "GDD accumulation function, intercept parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_b ; + fates_phen_gddthresh_b:units = "none" ; + fates_phen_gddthresh_b:long_name = "GDD accumulation function, multiplier parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_gddthresh_c ; + fates_phen_gddthresh_c:units = "none" ; + fates_phen_gddthresh_c:long_name = "GDD accumulation function, exponent parameter: gdd_thesh = a + b exp(c*ncd)" ; + double fates_phen_mindayson ; + fates_phen_mindayson:units = "days" ; + fates_phen_mindayson:long_name = "day threshold compared against days since leaves became on-allometry" ; + double fates_phen_ncolddayslim ; + fates_phen_ncolddayslim:units = "days" ; + fates_phen_ncolddayslim:long_name = "day threshold exceedance for temperature leaf-drop" ; + double fates_q10_froz ; + fates_q10_froz:units = "unitless" ; + fates_q10_froz:long_name = "Q10 for frozen-soil respiration rates" ; + double fates_q10_mr ; + fates_q10_mr:units = "unitless" ; + fates_q10_mr:long_name = "Q10 for maintenance respiration" ; + double fates_soil_salinity ; + fates_soil_salinity:units = "ppt" ; + fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; + double fates_trs_seedling2sap_par_timescale ; + fates_trs_seedling2sap_par_timescale:units = "days" ; + fates_trs_seedling2sap_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling to sapling transition rates" ; + double fates_trs_seedling_emerg_h2o_timescale ; + fates_trs_seedling_emerg_h2o_timescale:units = "days" ; + fates_trs_seedling_emerg_h2o_timescale:long_name = "Length of the window for the exponential moving average of smp used to calculate seedling emergence" ; + double fates_trs_seedling_mdd_timescale ; + fates_trs_seedling_mdd_timescale:units = "days" ; + fates_trs_seedling_mdd_timescale:long_name = "Length of the window for the exponential moving average of moisture deficit days used to calculate seedling mortality" ; + double fates_trs_seedling_mort_par_timescale ; + fates_trs_seedling_mort_par_timescale:units = "days" ; + fates_trs_seedling_mort_par_timescale:long_name = "Length of the window for the exponential moving average of par at the seedling layer used to calculate seedling mortality" ; + double fates_vai_top_bin_width ; + fates_vai_top_bin_width:units = "m2/m2" ; + fates_vai_top_bin_width:long_name = "width in VAI units of uppermost leaf+stem layer scattering element in each canopy layer" ; + double fates_vai_width_increase_factor ; + fates_vai_width_increase_factor:units = "unitless" ; + fates_vai_width_increase_factor:long_name = "factor by which each leaf+stem scattering element increases in VAI width (1 = uniform spacing)" ; + +// global attributes: + :history = "This file was generated by BatchPatchParams.py:\nCDL Base File = fates_params_default.cdl\nXML patch file = archive/api36.1.0_100224_pr1255-2.xml" ; +data: + + fates_history_ageclass_bin_edges = 0, 1, 2, 5, 10, 20, 50 ; + + fates_history_coageclass_bin_edges = 0, 5 ; + + fates_history_height_bin_edges = 0, 0.1, 0.3, 1, 3, 10 ; + + fates_history_damage_bin_edges = 0, 80 ; + + fates_history_sizeclass_bin_edges = 0, 5, 10, 15, 20, 30, 40, 50, 60, 70, + 80, 90, 100 ; + + fates_alloc_organ_id = 1, 2, 3, 6 ; + + fates_hydro_htftype_node = 1, 1, 1, 1 ; + + fates_pftname = + "broadleaf_evergreen_tropical_tree ", + "needleleaf_evergreen_extratrop_tree ", + "needleleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_tree ", + "broadleaf_hydrodecid_tropical_tree ", + "broadleaf_colddecid_extratrop_tree ", + "broadleaf_evergreen_extratrop_shrub ", + "broadleaf_hydrodecid_extratrop_shrub ", + "broadleaf_colddecid_extratrop_shrub ", + " broadleaf_evergreen_arctic_shrub ", + " broadleaf_colddecid_arctic_shrub ", + "arctic_c3_grass ", + "cool_c3_grass ", + "c4_grass " ; + + fates_hydro_organ_name = + "leaf ", + "stem ", + "transporting root ", + "absorbing root " ; + + fates_alloc_organ_name = + "leaf", + "fine root", + "sapwood", + "structure" ; + + fates_landuseclass_name = + "primaryland", + "secondaryland", + "rangeland", + "pastureland", + "cropland" ; + + fates_litterclass_name = + "twig ", + "small branch ", + "large branch ", + "trunk ", + "dead leaves ", + "live grass " ; + + fates_alloc_organ_priority = + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ; + + fates_alloc_storage_cushion = 1.2, 1.2, 1.2, 1.2, 2.4, 1.2, 1.2, 2.4, 1.2, + 1.5, 1.4, 1.2, 1.2, 1.2 ; + + fates_alloc_store_priority_frac = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.7, 0.6, 0.6, 0.8, 0.8 ; + + fates_allom_agb1 = 0.0673, 0.1364012, 0.0393057, 0.2653695, 0.0673, + 0.0728698, 0.06896, 0.06896, 0.06896, 0.06896, 0.06896, 0.001, 0.001, + 0.003 ; + + fates_allom_agb2 = 0.976, 0.9449041, 1.087335, 0.8321321, 0.976, 1.0373211, + 0.572, 0.572, 0.572, 0.5289883, 0.6853945, 1.6592, 1.6592, 1.3456 ; + + fates_allom_agb3 = 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, 1.94, + 2.1010352, 1.7628613, 1.248, 1.248, 1.869 ; + + fates_allom_agb4 = 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, 0.931, + 0.931, 0.931, 0.931, -999.9, -999.9, -999.9 ; + + fates_allom_agb_frac = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 1, 1, 1 ; + + fates_allom_amode = 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_blca_expnt_diff = -0.12, -0.34, -0.32, -0.22, -0.12, -0.35, 0, + 0, 0, 0, 0, -0.487, -0.487, -0.259 ; + + fates_allom_cmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_d2bl1 = 0.04, 0.07, 0.07, 0.01, 0.04, 0.07, 0.07, 0.07, 0.07, + 0.0481934, 0.0481934, 0.0004, 0.0004, 0.0012 ; + + fates_allom_d2bl2 = 1.6019679, 1.5234373, 1.3051237, 1.9621397, 1.6019679, + 1.3998939, 1.3, 1.3, 1.3, 1.0600586, 1.7176758, 1.7092, 1.7092, 1.5879 ; + + fates_allom_d2bl3 = 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, 0.55, + 0.55, 0.55, 0.3417, 0.3417, 0.9948 ; + + fates_allom_d2ca_coefficient_max = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2ca_coefficient_min = 0.2715891, 0.3693718, 1.0787259, + 0.0579297, 0.2715891, 1.1553612, 0.6568464, 0.6568464, 0.6568464, + 0.4363427, 0.3166497, 0.0408, 0.0408, 0.0862 ; + + fates_allom_d2h1 = 78.4087704, 306.842667, 106.8745821, 104.3586841, + 78.4087704, 31.4557047, 0.64, 0.64, 0.64, 0.8165625, 0.778125, 0.1812, + 0.1812, 0.3353 ; + + fates_allom_d2h2 = 0.8124383, 0.752377, 0.9471302, 1.1146973, 0.8124383, + 0.9734088, 0.37, 0.37, 0.37, 0.2316113, 0.4027002, 0.6384, 0.6384, 0.4235 ; + + fates_allom_d2h3 = 47.6666164, 196.6865691, 93.9790461, 160.6835089, + 47.6666164, 16.5928174, -999.9, -999.9, -999.9, -999.9, -999.9, -999.9, + -999.9, -999.9 ; + + fates_allom_dbh_maxheight = 1000, 1000, 1000, 1000, 1000, 1000, 3, 3, 2, + 2.4, 1.9, 20, 20, 30 ; + + fates_allom_dmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_fnrt_prof_a = 7, 7, 7, 7, 6, 6, 7, 7, 7, 7, 7, 11, 11, 11 ; + + fates_allom_fnrt_prof_b = 1, 2, 2, 1, 2, 2, 1.5, 1.5, 1.5, 1.5, 1.5, 2, 2, 2 ; + + fates_allom_fnrt_prof_mode = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 ; + + fates_allom_frbstor_repro = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_h2cd1 = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.95, 0.95, 0.95, 0.95, + 0.95, 1, 1, 1 ; + + fates_allom_h2cd2 = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_hmode = 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 3, 3, 3 ; + + fates_allom_l2fr = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.67, 0.67, 1.41 ; + + fates_allom_la_per_sa_int = 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, 0.8, + 0.8, 0.8, 0.8, 0.8, 0.8 ; + + fates_allom_la_per_sa_slp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_allom_lmode = 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 5, 5, 5 ; + + fates_allom_sai_scaler = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1, 0.1 ; + + fates_allom_smode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 ; + + fates_allom_stmode = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_allom_zroot_k = 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ; + + fates_allom_zroot_max_dbh = 100, 100, 100, 100, 100, 100, 2, 2, 2, 2, 2, 2, + 2, 2 ; + + fates_allom_zroot_max_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_allom_zroot_min_dbh = 1, 1, 1, 2.5, 2.5, 2.5, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_allom_zroot_min_z = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_c2b = 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_cnp_eca_alpha_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_decompmicc = 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280 ; + + fates_cnp_eca_km_nh4 = 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, 0.14, + 0.14, 0.14, 0.14, 0.14, 0.14 ; + + fates_cnp_eca_km_no3 = 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, + 0.27, 0.27, 0.27, 0.27, 0.27 ; + + fates_cnp_eca_km_p = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_eca_km_ptase = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_eca_lambda_ptase = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_eca_vmax_ptase = 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, + 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09, 5e-09 ; + + fates_cnp_nfix1 = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_nitr_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_phos_store_ratio = 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, + 1.5, 1.5, 1.5, 1.5, 1.5 ; + + fates_cnp_pid_kd = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.1, 0.1, 0.1 ; + + fates_cnp_pid_ki = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_pid_kp = 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, + 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005 ; + + fates_cnp_prescribed_nuptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_prescribed_puptake = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_store_ovrflw_frac = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_cnp_turnover_nitr_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_turnover_phos_retrans = + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_cnp_vmax_nh4 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_no3 = 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, + 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09, 2.5e-09 ; + + fates_cnp_vmax_p = 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, + 5e-10, 5e-10, 5e-10, 5e-10, 5e-10, 5e-10 ; + + fates_damage_frac = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_damage_mort_p1 = 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 ; + + fates_damage_mort_p2 = 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, 5.5, + 5.5, 5.5, 5.5, 5.5 ; + + fates_damage_recovery_scalar = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_dev_arbitrary_pft = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_fire_alpha_SH = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2 ; + + fates_fire_bark_scaler = 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, + 0.07, 0.07, 0.07, 0.07, 0.07, 0.07 ; + + fates_fire_crown_kill = 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, + 0.775, 0.775, 0.775, 0.775, 0.775, 0.775, 0.775 ; + + fates_frag_fnrt_fcel = 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_frag_fnrt_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_fnrt_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_fcel = 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_frag_leaf_flab = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_leaf_flig = 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, + 0.25, 0.25, 0.25, 0.25, 0.25 ; + + fates_frag_seed_decay_rate = 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, 0.51, + 0.51, 0.74, 0.46, 0.35, 0.51, 0.51 ; + + fates_grperc = 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.12, + 0.11, 0.16, 0.11, 0.11 ; + + fates_hydro_avuln_gs = 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, + 2.5, 2.5, 2.5, 2.5 ; + + fates_hydro_avuln_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_hydro_epsil_node = + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_hydro_fcap_node = + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, + 0.08, 0.08, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_k_lwp = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_hydro_kmax_node = + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999 ; + + fates_hydro_p50_gs = -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, -1.5, + -1.5, -1.5, -1.5, -1.5, -1.5 ; + + fates_hydro_p50_node = + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, -2.25, + -2.25, -2.25, -2.25, -2.25 ; + + fates_hydro_p_taper = 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, + 0.333, 0.333, 0.333, 0.333, 0.333, 0.333, 0.333 ; + + fates_hydro_pinot_node = + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, -1.465984, + -1.465984, -1.465984, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, -1.22807, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, -1.043478, + -1.043478, -1.043478 ; + + fates_hydro_pitlp_node = + -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, -1.67, + -1.67, -1.67, -1.67, -1.67, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, -1.4, + -1.4, -1.4, + -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, -1.2, + -1.2, -1.2 ; + + fates_hydro_resid_node = + 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, 0.16, + 0.16, 0.16, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, + 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, + 0.11, 0.11 ; + + fates_hydro_rfrac_stem = 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, + 0.625, 0.625, 0.625, 0.625, 0.625, 0.625, 0.625 ; + + fates_hydro_rs2 = 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, + 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001 ; + + fates_hydro_srl = 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25 ; + + fates_hydro_thetas_node = + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, 0.65, + 0.65, 0.65, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, + 0.75, 0.75 ; + + fates_hydro_vg_alpha_node = + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, + 0.12, 0.12 ; + + fates_hydro_vg_m_node = + 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, + 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, + 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, + 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_hydro_vg_n_node = + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ; + + fates_landuse_grazing_palatability = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 ; + + fates_landuse_harvest_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, + 1, 1, 1 ; + + fates_landuse_luc_frac_burned = 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_landuse_luc_frac_exported = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.2, 0.2, + 0.2, 0.2, 0.2, 0, 0, 0 ; + + fates_landuse_luc_pprod10 = 1, 0.75, 0.75, 0.75, 1, 0.75, 1, 1, 1, 1, 1, 1, + 1, 1 ; + + fates_leaf_agross_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_c3psn = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 ; + + fates_leaf_fnps = 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, 0.15, + 0.15, 0.15, 0.15, 0.15, 0.15 ; + + fates_leaf_jmaxha = 43540, 43540, 43540, 43540, 43540, 43540, 43540, 43540, + 43540, 43540, 43540, 43540, 43540, 43540 ; + + fates_leaf_jmaxhd = 152040, 152040, 152040, 152040, 152040, 152040, 152040, + 152040, 152040, 152040, 152040, 152040, 152040, 152040 ; + + fates_leaf_jmaxse = 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, + 495, 495, 495 ; + + fates_leaf_slamax = 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.0954, 0.012, + 0.03, 0.03, 0.012, 0.032, 0.05, 0.05, 0.05 ; + + fates_leaf_slatop = 0.012, 0.005, 0.024, 0.009, 0.03, 0.03, 0.012, 0.03, + 0.03, 0.01, 0.032, 0.027, 0.05, 0.05 ; + + fates_leaf_stomatal_btran_model = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_leaf_stomatal_intercept = 10000, 10000, 10000, 10000, 10000, 10000, + 10000, 10000, 10000, 10000, 10000, 10000, 10000, 40000 ; + + fates_leaf_stomatal_slope_ballberry = 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ; + + fates_leaf_stomatal_slope_medlyn = 4.1, 2.3, 2.3, 4.1, 4.4, 4.4, 4.7, 4.7, + 4.7, 4.7, 4.7, 2.2, 5.3, 1.6 ; + + fates_leaf_vcmax25top = + 50, 62, 39, 61, 58, 58, 62, 54, 54, 38, 54, 86, 78, 78 ; + + fates_leaf_vcmaxha = 65330, 65330, 65330, 65330, 65330, 65330, 65330, 65330, + 65330, 65330, 65330, 65330, 65330, 65330 ; + + fates_leaf_vcmaxhd = 149250, 149250, 149250, 149250, 149250, 149250, 149250, + 149250, 149250, 149250, 149250, 149250, 149250, 149250 ; + + fates_leaf_vcmaxse = 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, + 485, 485, 485 ; + + fates_leafn_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963 ; + + fates_leafn_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_leaf_atkin2017_baserate = 1.756, 1.4995, 1.4995, 1.756, + 1.756, 1.756, 2.0749, 2.0749, 2.0749, 2.0749, 2.0749, 2.1956, 2.1956, + 2.1956 ; + + fates_maintresp_leaf_ryan1991_baserate = 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, + 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06, 2.525e-06 ; + + fates_maintresp_leaf_vert_scaler_coeff1 = 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, 0.00963, + 0.00963, 0.00963, 0.00963 ; + + fates_maintresp_leaf_vert_scaler_coeff2 = 2.43, 2.43, 2.43, 2.43, 2.43, + 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43, 2.43 ; + + fates_maintresp_reduction_curvature = 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, + 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ; + + fates_maintresp_reduction_intercept = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_maintresp_reduction_upthresh = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_mort_bmort = 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, 0.014, + 0.014, 0.016, 0.01, 0.014, 0.014, 0.014 ; + + 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, + 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 = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_prescribed_canopy = 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, + 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194, 0.0194 ; + + fates_mort_prescribed_understory = 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, + 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025 ; + + fates_mort_r_age_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_r_size_senescence = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_mort_scalar_coldstress = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3.5, 2.3, 3, 3 ; + + fates_mort_scalar_cstarvation = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.57, 0.6, 0.6, 0.6 ; + + fates_mort_scalar_hydrfailure = 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, + 0.6, 0.8, 0.6, 0.6, 0.6 ; + + fates_mort_upthresh_cstarvation = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_nonhydro_smpsc = -255000, -255000, -255000, -255000, -255000, -255000, + -255000, -255000, -255000, -255000, -255000, -255000, -255000, -255000 ; + + fates_nonhydro_smpso = -66000, -66000, -66000, -66000, -66000, -66000, + -66000, -66000, -66000, -66000, -66000, -66000, -66000, -66000 ; + + fates_phen_cold_size_threshold = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_drought_threshold = -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, + -152957.4, -152957.4, -152957.4, -152957.4 ; + + fates_phen_flush_fraction = _, _, 0.5, _, 0.5, 0.5, _, 0.5, 0.5, _, 0.5, + 0.5, 0.5, 0.5 ; + + fates_phen_fnrt_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_phen_leaf_habit = 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 2, 2, 3, 3 ; + + fates_phen_mindaysoff = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100 ; + + fates_phen_moist_threshold = -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, + -122365.9, -122365.9, -122365.9, -122365.9 ; + + fates_phen_stem_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_prescribed_npp_canopy = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, + 0.4, 0.4, 0.4, 0.4, 0.4 ; + + fates_prescribed_npp_understory = 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, + 0.03125, 0.03125 ; + + fates_rad_leaf_clumping_index = 0.85, 0.85, 0.8, 0.85, 0.85, 0.9, 0.85, 0.9, + 0.9, 0.85, 0.9, 0.75, 0.75, 0.75 ; + + fates_rad_leaf_rhonir = 0.46, 0.41, 0.39, 0.46, 0.41, 0.41, 0.46, 0.41, + 0.41, 0.46, 0.41, 0.28, 0.28, 0.28 ; + + fates_rad_leaf_rhovis = 0.11, 0.09, 0.08, 0.11, 0.08, 0.08, 0.11, 0.08, + 0.08, 0.11, 0.08, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_taunir = 0.33, 0.32, 0.42, 0.33, 0.43, 0.43, 0.33, 0.43, + 0.43, 0.33, 0.43, 0.4, 0.4, 0.4 ; + + fates_rad_leaf_tauvis = 0.06, 0.04, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.05, 0.05, 0.05 ; + + fates_rad_leaf_xl = 0.32, 0.01, 0.01, 0.32, 0.2, 0.59, 0.32, 0.59, 0.59, + 0.32, 0.59, -0.23, -0.23, -0.23 ; + + fates_rad_stem_rhonir = 0.49, 0.36, 0.36, 0.49, 0.49, 0.49, 0.49, 0.49, + 0.49, 0.49, 0.49, 0.53, 0.53, 0.53 ; + + fates_rad_stem_rhovis = 0.21, 0.12, 0.12, 0.21, 0.21, 0.21, 0.21, 0.21, + 0.21, 0.21, 0.21, 0.31, 0.31, 0.31 ; + + fates_rad_stem_taunir = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.25, 0.25, 0.25 ; + + fates_rad_stem_tauvis = 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, + 0.001, 0.001, 0.001, 0.001, 0.12, 0.12, 0.12 ; + + fates_recruit_height_min = 1.3, 1.3, 1.3, 1.3, 1.3, 1.3, 0.2, 0.2, 0.2, 0.8, + 0.8, 0.11, 0.2, 0.2 ; + + fates_recruit_init_density = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.16, 0.2, 0.2, 0.2, 0.2 ; + + fates_recruit_prescribed_rate = 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, + 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02 ; + + fates_recruit_seed_alloc = 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, + 0.07, 0.1, 0, 0, 0 ; + + fates_recruit_seed_alloc_mature = 0, 0, 0, 0, 0, 0, 0.9, 0.9, 0.9, 0.9, 0.9, + 0.25, 0.25, 0.2 ; + + fates_recruit_seed_dbh_repro_threshold = 90, 80, 80, 80, 90, 80, 3, 3, 2, + 2.4, 1.9, 3, 3, 3 ; + + fates_recruit_seed_germination_rate = 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.4, 0.49, 0.29, 0.5, 0.5 ; + + fates_recruit_seed_supplement = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + + fates_seed_dispersal_fraction = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_max_dist = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_scale = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_seed_dispersal_pdf_shape = _, _, _, _, _, _, _, _, _, _, _, _, _, _ ; + + fates_stoich_nitr = + 0.033, 0.029, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, 0.04, 0.033, 0.04, + 0.04, 0.04, 0.04, + 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, 0.024, + 0.024, 0.024, 0.024, 0.024, + 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, 1e-08, + 1e-08, 1e-08, 1e-08, 1e-08, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, 0.0047, + 0.0047, 0.0047, 0.0047, 0.0047, 0.0047 ; + + fates_stoich_phos = + 0.0033, 0.0029, 0.004, 0.0033, 0.004, 0.004, 0.0033, 0.004, 0.004, 0.0033, + 0.004, 0.004, 0.004, 0.004, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 0.0024, 0.0024, 0.0024, 0.0024, 0.0024, + 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, 1e-09, + 1e-09, 1e-09, 1e-09, 1e-09, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, + 0.00047, 0.00047, 0.00047, 0.00047, 0.00047, 0.00047 ; + + fates_trim_inc = 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, 0.03, + 0.03, 0.03, 0.03, 0.03 ; + + fates_trim_limit = 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, + 0.3, 0.3, 0.3 ; + + fates_trs_repro_alloc_a = 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, + 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049, 0.0049 ; + + fates_trs_repro_alloc_b = -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, -2.6171, + -2.6171 ; + + fates_trs_repro_frac_seed = 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, 0.24, + 0.24, 0.24, 0.24, 0.24, 0.24, 0.24 ; + + fates_trs_seedling_a_emerg = 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, + 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003, 0.0003 ; + + fates_trs_seedling_b_emerg = 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, + 1.2, 1.2, 1.2, 1.2, 1.2 ; + + fates_trs_seedling_background_mort = 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371, + 0.1085371, 0.1085371, 0.1085371, 0.1085371, 0.1085371 ; + + fates_trs_seedling_h2o_mort_a = 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, 4.070565e-17, + 4.070565e-17 ; + + fates_trs_seedling_h2o_mort_b = -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11, -6.390757e-11, + -6.390757e-11, -6.390757e-11, -6.390757e-11 ; + + fates_trs_seedling_h2o_mort_c = 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, 1.268992e-05, + 1.268992e-05 ; + + fates_trs_seedling_light_mort_a = -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694, -0.009897694, -0.009897694, -0.009897694, -0.009897694, + -0.009897694 ; + + fates_trs_seedling_light_mort_b = -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, -7.154063, + -7.154063, -7.154063, -7.154063, -7.154063, -7.154063 ; + + fates_trs_seedling_light_rec_a = 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, + 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007, 0.007 ; + + fates_trs_seedling_light_rec_b = 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, + 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615, 0.8615 ; + + fates_trs_seedling_mdd_crit = 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, 1400000, + 1400000 ; + + fates_trs_seedling_par_crit_germ = 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, + 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656, 0.656 ; + + fates_trs_seedling_psi_crit = -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, -251995.7, + -251995.7, -251995.7, -251995.7, -251995.7 ; + + fates_trs_seedling_psi_emerg = -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, -15744.65, + -15744.65, -15744.65, -15744.65, -15744.65 ; + + fates_trs_seedling_root_depth = 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, + 0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06 ; + + fates_turb_displar = 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, 0.67, + 0.67, 0.67, 0.67, 0.67, 0.67 ; + + fates_turb_leaf_diameter = 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, + 0.04, 0.04, 0.04, 0.04, 0.04, 0.04 ; + + fates_turb_z0mr = 0.075, 0.055, 0.055, 0.075, 0.055, 0.055, 0.12, 0.12, + 0.12, 0.12, 0.12, 0.12, 0.12, 0.12 ; + + fates_turnover_branch = 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, + 150, 0, 0, 0 ; + + fates_turnover_fnrt = 1, 2, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_canopy = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_leaf_ustory = + 1.5, 4, 1, 1.5, 1, 1, 1.5, 1, 1, 1.5, 1, 1, 1, 1 ; + + fates_turnover_senleaf_fdrought = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; + + fates_wood_density = 0.548327, 0.44235, 0.454845, 0.754336, 0.548327, + 0.566452, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7 ; + + fates_woody = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 ; + + fates_hlm_pft_map = + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0.1, 0.8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ; + + fates_fire_FBD = 15.4, 16.8, 19.6, 999, 4, 4 ; + + fates_fire_low_moisture_Coeff = 1.12, 1.09, 0.98, 0.8, 1.15, 1.15 ; + + fates_fire_low_moisture_Slope = 0.62, 0.72, 0.85, 0.8, 0.62, 0.62 ; + + fates_fire_mid_moisture = 0.72, 0.51, 0.38, 1, 0.8, 0.8 ; + + fates_fire_mid_moisture_Coeff = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_mid_moisture_Slope = 2.35, 1.47, 1.06, 0.8, 3.2, 3.2 ; + + fates_fire_min_moisture = 0.18, 0.12, 0, 0, 0.24, 0.24 ; + + fates_fire_SAV = 13, 3.58, 0.98, 0.2, 66, 66 ; + + fates_frag_maxdecomp = 0.52, 0.383, 0.383, 0.19, 1, 999 ; + + fates_frag_cwd_frac = 0.045, 0.075, 0.21, 0.67 ; + + fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 11 ; + + fates_landuse_grazing_rate = 0, 0, 0, 0, 0 ; + + fates_max_nocomp_pfts_by_landuse = 4, 4, 1, 1, 1 ; + + fates_maxpatches_by_landuse = 9, 4, 1, 1, 1 ; + + fates_canopy_closure_thresh = 0.8 ; + + fates_cnp_eca_plant_escalar = 1.25e-05 ; + + fates_cohort_age_fusion_tol = 0.08 ; + + fates_cohort_size_fusion_tol = 0.08 ; + + fates_comp_excln = -1 ; + + fates_damage_canopy_layer_code = 1 ; + + fates_damage_event_code = 1 ; + + fates_dev_arbitrary = _ ; + + fates_fire_active_crown_fire = 0 ; + + fates_fire_cg_strikes = 0.2 ; + + fates_fire_drying_ratio = 66000 ; + + fates_fire_durat_slope = -11.06 ; + + fates_fire_fdi_alpha = 0.00037 ; + + fates_fire_fuel_energy = 18000 ; + + fates_fire_max_durat = 240 ; + + fates_fire_miner_damp = 0.41739 ; + + fates_fire_miner_total = 0.055 ; + + fates_fire_nignitions = 15 ; + + fates_fire_part_dens = 513 ; + + fates_fire_threshold = 50 ; + + fates_frag_cwd_fcel = 0.76 ; + + fates_frag_cwd_flig = 0.24 ; + + fates_hydro_kmax_rsurf1 = 20 ; + + fates_hydro_kmax_rsurf2 = 0.0001 ; + + fates_hydro_psi0 = 0 ; + + fates_hydro_psicap = -0.6 ; + + fates_landuse_grazing_carbon_use_eff = 0 ; + + fates_landuse_grazing_maxheight = 1 ; + + fates_landuse_grazing_nitrogen_use_eff = 0.25 ; + + fates_landuse_grazing_phosphorus_use_eff = 0.5 ; + + fates_landuse_logging_coll_under_frac = 0. ; + + fates_landuse_logging_collateral_frac = 0. ; + + fates_landuse_logging_dbhmax = _ ; + + fates_landuse_logging_dbhmax_infra = 0 ; + + fates_landuse_logging_dbhmin = 0 ; + + fates_landuse_logging_direct_frac = 1. ; + + fates_landuse_logging_event_code = -30 ; + + fates_landuse_logging_export_frac = 0.8 ; + + fates_landuse_logging_mechanical_frac = 0. ; + + fates_leaf_photo_temp_acclim_thome_time = 30 ; + + fates_leaf_photo_temp_acclim_timescale = 30 ; + + fates_leaf_theta_cj_c3 = 0.999 ; + + fates_leaf_theta_cj_c4 = 0.999 ; + + fates_maintresp_nonleaf_baserate = 2.525e-06 ; + + fates_maxcohort = 100 ; + + fates_mort_disturb_frac = 1 ; + + fates_mort_understorey_death = 0.55983 ; + + fates_patch_fusion_tol = 0.05 ; + + fates_phen_chilltemp = 5 ; + + fates_phen_coldtemp = 7.5 ; + + fates_phen_gddthresh_a = -68 ; + + fates_phen_gddthresh_b = 638 ; + + fates_phen_gddthresh_c = -0.01 ; + + fates_phen_mindayson = 90 ; + + fates_phen_ncolddayslim = 5 ; + + fates_q10_froz = 1.5 ; + + fates_q10_mr = 1.5 ; + + fates_soil_salinity = 0.4 ; + + fates_trs_seedling2sap_par_timescale = 32 ; + + fates_trs_seedling_emerg_h2o_timescale = 7 ; + + fates_trs_seedling_mdd_timescale = 126 ; + + fates_trs_seedling_mort_par_timescale = 32 ; + + fates_vai_top_bin_width = 1 ; + + fates_vai_width_increase_factor = 1 ; +} diff --git a/parameter_files/archive/api40.0.0_pr1355_patch_params.xml b/parameter_files/archive/api40.0.0_pr1355_patch_params.xml new file mode 100644 index 0000000000..3841546ee5 --- /dev/null +++ b/parameter_files/archive/api40.0.0_pr1355_patch_params.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + archive/api39.0.0_050825_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + fates_phen_evergreen + + + fates_phen_season_decid + + + fates_phen_stress_decid + + + fates_phen_leaf_habit + fates_pft + flag + Flag for leaf phenology habit. 1 - evergreen; 2 - season (cold) deciduous; 3 - stress (hydro) deciduous; 4 - stress (hydro) semi-deciduous + 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 2, 2, 3, 3 + + + diff --git a/parameter_files/archive/api40.0.0_pr1358_patch_params.xml b/parameter_files/archive/api40.0.0_pr1358_patch_params.xml new file mode 100644 index 0000000000..ae8665ab4f --- /dev/null +++ b/parameter_files/archive/api40.0.0_pr1358_patch_params.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + fates_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + -1 + + + diff --git a/parameter_files/archive/api40.0.0_pr1359_patch_params.xml b/parameter_files/archive/api40.0.0_pr1359_patch_params.xml new file mode 100644 index 0000000000..1ea735f270 --- /dev/null +++ b/parameter_files/archive/api40.0.0_pr1359_patch_params.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + fates_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + 0. + + + 0. + + + 0 + + + 0 + + + 1. + + + 0. + + + diff --git a/parameter_files/archive/api41.0.0_pr1444_patch_params.xml b/parameter_files/archive/api41.0.0_pr1444_patch_params.xml new file mode 100644 index 0000000000..6765ff04da --- /dev/null +++ b/parameter_files/archive/api41.0.0_pr1444_patch_params.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + archive/api40.0.0_060625_params_default.cdl + fates_params_default.cdl + 1,2,3,4,5,6,7,8,9,10,11,12,13,14 + + + fates_leaf_theta_cj_c3 + + + fates_leaf_theta_cj_c4 + + + -999, -999, -999, -999, 13 + + + fates_rxfire_temp_upthreshold + scalar + degree C + maximum temprature threshold above which prescribed fire is disallowed + 30 + + + fates_rxfire_temp_lwthreshold + scalar + degree C + minimum temprature threshold below which prescribed fire is disallowed + 5 + + + fates_rxfire_rh_upthreshold + scalar + % + maximum relative humidity threshold above which prescribed fire is disallowed + 55 + + + fates_rxfire_rh_lwthreshold + scalar + % + minimum relative humidity threshold below which prescribed fire is disallowed + 30 + + + fates_rxfire_wind_upthreshold + scalar + % + maximum wind speed threshold above which prescribed fire is disallowed + 10 + + + fates_rxfire_wind_lwthreshold + scalar + % + minimum wind speed threshold below which prescribed fire is disallowed + 2 + + + fates_rxfire_AB + scalar + fraction/day + daily burn capacity of prescribed fire + 0.01 + + + fates_rxfire_min_threshold + scalar + kJ/m/s or kW/m + minimum energy threshold at or above which prescribed fire is disallowed + 50 + + + fates_rxfire_max_threshold + scalar + kJ/m/s or kW/m + maximum energy threshold at or above which prescribed fire is disallowed + 500 + + + fates_rxfire_fuel_min + scalar + kgC/m2 + minimum fuel load at or below which prescribed fire is disallowed + 0.5 + + + fates_rxfire_fuel_max + scalar + kgC/m2 + maximum fuel load at or above which prescribed fire is disallowed + 1.5 + + + fates_rxfire_min_frac + scalar + fraction + minimum fraction of land needs to be burnable to allow rx fire + 0.1 + + + diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 55d1d0d41c..e899d3ff7b 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -492,30 +492,24 @@ variables: double fates_phen_drought_threshold(fates_pft) ; fates_phen_drought_threshold:units = "m3/m3 or mm" ; fates_phen_drought_threshold:long_name = "threshold for drought phenology (or lower threshold for semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; - double fates_phen_evergreen(fates_pft) ; - fates_phen_evergreen:units = "logical flag" ; - fates_phen_evergreen:long_name = "Binary flag for evergreen leaf habit" ; double fates_phen_flush_fraction(fates_pft) ; fates_phen_flush_fraction:units = "fraction" ; fates_phen_flush_fraction:long_name = "Upon bud-burst, the maximum fraction of storage carbon used for flushing leaves" ; double fates_phen_fnrt_drop_fraction(fates_pft) ; fates_phen_fnrt_drop_fraction:units = "fraction" ; fates_phen_fnrt_drop_fraction:long_name = "fraction of fine roots to drop during drought/cold" ; + double fates_phen_leaf_habit(fates_pft) ; + fates_phen_leaf_habit:units = "flag" ; + fates_phen_leaf_habit:long_name = "Flag for leaf phenology habit. 1 - evergreen; 2 - season (cold) deciduous; 3 - stress (hydro) deciduous; 4 - stress (hydro) semi-deciduous" ; double fates_phen_mindaysoff(fates_pft) ; fates_phen_mindaysoff:units = "days" ; fates_phen_mindaysoff:long_name = "day threshold compared against days since leaves abscised (shed)" ; double fates_phen_moist_threshold(fates_pft) ; fates_phen_moist_threshold:units = "m3/m3 or mm" ; fates_phen_moist_threshold:long_name = "upper threshold for drought phenology (only for drought semi-deciduous PFTs); the quantity depends on the sign: if positive, the threshold is volumetric soil moisture (m3/m3). If negative, the threshold is soil matric potentical (mm)" ; - double fates_phen_season_decid(fates_pft) ; - fates_phen_season_decid:units = "logical flag" ; - fates_phen_season_decid:long_name = "Binary flag for seasonal-deciduous leaf habit" ; double fates_phen_stem_drop_fraction(fates_pft) ; fates_phen_stem_drop_fraction:units = "fraction" ; fates_phen_stem_drop_fraction:long_name = "fraction of stems to drop for non-woody species during drought/cold" ; - double fates_phen_stress_decid(fates_pft) ; - fates_phen_stress_decid:units = "logical flag" ; - fates_phen_stress_decid:long_name = "Flag for stress/drought-deciduous leaf habit. 0 - not stress deciduous; 1 - default drought deciduous (two target states only, fully flushed or fully abscised); 2 - semi-deciduous" ; double fates_prescribed_npp_canopy(fates_pft) ; fates_prescribed_npp_canopy:units = "kgC / m^2 / yr" ; fates_prescribed_npp_canopy:long_name = "NPP per unit crown area of canopy trees for prescribed physiology mode" ; @@ -852,12 +846,6 @@ variables: double fates_leaf_photo_temp_acclim_timescale ; fates_leaf_photo_temp_acclim_timescale:units = "days" ; fates_leaf_photo_temp_acclim_timescale:long_name = "Length of the window for the exponential moving average (ema) of vegetation temperature used in photosynthesis temperature acclimation (used if fates_maintresp_leaf_model=2 or fates_leaf_photo_tempsens_model = 2)" ; - double fates_leaf_theta_cj_c3 ; - fates_leaf_theta_cj_c3:units = "unitless" ; - fates_leaf_theta_cj_c3:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; - double fates_leaf_theta_cj_c4 ; - fates_leaf_theta_cj_c4:units = "unitless" ; - fates_leaf_theta_cj_c4:long_name = "SOON TO BE DEPRECATED, DO NOT USE" ; double fates_maintresp_nonleaf_baserate ; fates_maintresp_nonleaf_baserate:units = "gC/gN/s" ; fates_maintresp_nonleaf_baserate:long_name = "Base maintenance respiration rate for plant tissues, using Ryan 1991" ; @@ -866,7 +854,7 @@ variables: fates_maxcohort:long_name = "maximum number of cohorts per patch. Actual number of cohorts also depend on cohort fusion tolerances" ; double fates_mort_disturb_frac ; fates_mort_disturb_frac:units = "fraction" ; - fates_mort_disturb_frac:long_name = "fraction of canopy mortality that results in disturbance (i.e. transfer of area from new to old patch)" ; + fates_mort_disturb_frac:long_name = "fraction of canopy mortality that results in disturbance (i.e. transfer of area from old to new patch)" ; double fates_mort_understorey_death ; fates_mort_understorey_death:units = "fraction" ; fates_mort_understorey_death:long_name = "fraction of plants in understorey cohort impacted by overstorey tree-fall" ; @@ -900,6 +888,42 @@ variables: double fates_q10_mr ; fates_q10_mr:units = "unitless" ; fates_q10_mr:long_name = "Q10 for maintenance respiration" ; + double fates_rxfire_AB ; + fates_rxfire_AB:units = "fraction/day" ; + fates_rxfire_AB:long_name = "daily burn capacity of prescribed fire" ; + double fates_rxfire_fuel_max ; + fates_rxfire_fuel_max:units = "kgC/m2" ; + fates_rxfire_fuel_max:long_name = "maximum fuel load at or above which prescribed fire is disallowed" ; + double fates_rxfire_fuel_min ; + fates_rxfire_fuel_min:units = "kgC/m2" ; + fates_rxfire_fuel_min:long_name = "minimum fuel load at or below which prescribed fire is disallowed" ; + double fates_rxfire_max_threshold ; + fates_rxfire_max_threshold:units = "kJ/m/s or kW/m" ; + fates_rxfire_max_threshold:long_name = "maximum energy threshold at or above which prescribed fire is disallowed" ; + double fates_rxfire_min_frac ; + fates_rxfire_min_frac:units = "fraction" ; + fates_rxfire_min_frac:long_name = "minimum fraction of land needs to be burnable to allow rx fire" ; + double fates_rxfire_min_threshold ; + fates_rxfire_min_threshold:units = "kJ/m/s or kW/m" ; + fates_rxfire_min_threshold:long_name = "minimum energy threshold at or above which prescribed fire is disallowed" ; + double fates_rxfire_rh_lwthreshold ; + fates_rxfire_rh_lwthreshold:units = "%" ; + fates_rxfire_rh_lwthreshold:long_name = "minimum relative humidity threshold below which prescribed fire is disallowed" ; + double fates_rxfire_rh_upthreshold ; + fates_rxfire_rh_upthreshold:units = "%" ; + fates_rxfire_rh_upthreshold:long_name = "maximum relative humidity threshold above which prescribed fire is disallowed" ; + double fates_rxfire_temp_lwthreshold ; + fates_rxfire_temp_lwthreshold:units = "degree C" ; + fates_rxfire_temp_lwthreshold:long_name = "minimum temprature threshold below which prescribed fire is disallowed" ; + double fates_rxfire_temp_upthreshold ; + fates_rxfire_temp_upthreshold:units = "degree C" ; + fates_rxfire_temp_upthreshold:long_name = "maximum temprature threshold above which prescribed fire is disallowed" ; + double fates_rxfire_wind_lwthreshold ; + fates_rxfire_wind_lwthreshold:units = "%" ; + fates_rxfire_wind_lwthreshold:long_name = "minimum wind speed threshold below which prescribed fire is disallowed" ; + double fates_rxfire_wind_upthreshold ; + fates_rxfire_wind_upthreshold:units = "%" ; + fates_rxfire_wind_upthreshold:long_name = "maximum wind speed threshold above which prescribed fire is disallowed" ; double fates_soil_salinity ; fates_soil_salinity:units = "ppt" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; @@ -1471,13 +1495,13 @@ data: -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4, -152957.4 ; - fates_phen_evergreen = 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 ; - fates_phen_flush_fraction = _, _, 0.5, _, 0.5, 0.5, _, 0.5, 0.5, _, 0.5, 0.5, 0.5, 0.5 ; fates_phen_fnrt_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; + fates_phen_leaf_habit = 1, 1, 2, 1, 3, 2, 1, 3, 2, 1, 2, 2, 3, 3 ; + fates_phen_mindaysoff = 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 ; @@ -1485,12 +1509,8 @@ data: -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9, -122365.9 ; - fates_phen_season_decid = 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0 ; - fates_phen_stem_drop_fraction = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; - fates_phen_stress_decid = 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1 ; - fates_prescribed_npp_canopy = 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4 ; @@ -1716,7 +1736,7 @@ data: fates_frag_cwd_frac = 0.045, 0.075, 0.21, 0.67 ; - fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 11 ; + fates_landuse_crop_lu_pft_vector = -999, -999, -999, -999, 13 ; fates_landuse_grazing_rate = 0, 0, 0, 0, 0 ; @@ -1732,7 +1752,7 @@ data: fates_cohort_size_fusion_tol = 0.08 ; - fates_comp_excln = 3 ; + fates_comp_excln = -1 ; fates_damage_canopy_layer_code = 1 ; @@ -1784,32 +1804,28 @@ data: fates_landuse_grazing_phosphorus_use_eff = 0.5 ; - fates_landuse_logging_coll_under_frac = 0.55983 ; + fates_landuse_logging_coll_under_frac = 0 ; - fates_landuse_logging_collateral_frac = 0.05 ; + fates_landuse_logging_collateral_frac = 0 ; fates_landuse_logging_dbhmax = _ ; - fates_landuse_logging_dbhmax_infra = 35 ; + fates_landuse_logging_dbhmax_infra = 0 ; - fates_landuse_logging_dbhmin = 50 ; + fates_landuse_logging_dbhmin = 0 ; - fates_landuse_logging_direct_frac = 0.15 ; + fates_landuse_logging_direct_frac = 1 ; fates_landuse_logging_event_code = -30 ; fates_landuse_logging_export_frac = 0.8 ; - fates_landuse_logging_mechanical_frac = 0.05 ; + fates_landuse_logging_mechanical_frac = 0 ; fates_leaf_photo_temp_acclim_thome_time = 30 ; fates_leaf_photo_temp_acclim_timescale = 30 ; - fates_leaf_theta_cj_c3 = 0.999 ; - - fates_leaf_theta_cj_c4 = 0.999 ; - fates_maintresp_nonleaf_baserate = 2.525e-06 ; fates_maxcohort = 100 ; @@ -1838,6 +1854,30 @@ data: fates_q10_mr = 1.5 ; + fates_rxfire_AB = 0.01 ; + + fates_rxfire_fuel_max = 1.5 ; + + fates_rxfire_fuel_min = 0.5 ; + + fates_rxfire_max_threshold = 500 ; + + fates_rxfire_min_frac = 0.1 ; + + fates_rxfire_min_threshold = 50 ; + + fates_rxfire_rh_lwthreshold = 30 ; + + fates_rxfire_rh_upthreshold = 55 ; + + fates_rxfire_temp_lwthreshold = 5 ; + + fates_rxfire_temp_upthreshold = 30 ; + + fates_rxfire_wind_lwthreshold = 2 ; + + fates_rxfire_wind_upthreshold = 10 ; + fates_soil_salinity = 0.4 ; fates_trs_seedling2sap_par_timescale = 32 ; diff --git a/parteh/PRTAllometricCNPMod.F90 b/parteh/PRTAllometricCNPMod.F90 index da049161a9..47d6d95443 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -52,7 +52,7 @@ module PRTAllometricCNPMod use FatesIntegratorsMod , only : Euler use FatesConstantsMod , only : calloc_abs_error use FatesConstantsMod , only : nearzero - use FatesConstantsMod , only : itrue + use FatesConstantsMod , only : ievergreen use FatesConstantsMod , only : fates_unset_r8 use FatesConstantsMod , only : fates_unset_int use FatesConstantsMod , only : sec_per_day @@ -1039,7 +1039,7 @@ subroutine CNPPrioritizedReplacement(this,c_gain, n_gain, p_gain, target_c) ! Also, dont allocate to replace turnover if this is not evergreen ! (this prevents accidental re-flushing on the day they drop) if( ( any(leaf_status == [leaves_off,leaves_shedding]) .or. & - (prt_params%evergreen(ipft) /= itrue) ) & + (prt_params%phen_leaf_habit(ipft) /= ievergreen) ) & .and. (i_org == leaf_organ)) cycle ! The priority code associated with this organ diff --git a/parteh/PRTAllometricCarbonMod.F90 b/parteh/PRTAllometricCarbonMod.F90 index 500140b2c8..c5e46f783e 100644 --- a/parteh/PRTAllometricCarbonMod.F90 +++ b/parteh/PRTAllometricCarbonMod.F90 @@ -60,6 +60,7 @@ module PRTAllometricCarbonMod use FatesConstantsMod , only : leaves_on use FatesConstantsMod , only : leaves_off use FatesConstantsMod , only : leaves_shedding + use FatesConstantsMod , only : ihard_season_decid use FatesConstantsMod , only : ihard_stress_decid use FatesConstantsMod , only : isemi_stress_decid @@ -433,10 +434,10 @@ subroutine DailyPRTAllometricCarbon(this,phase) elongf_fnrt = this%bc_in(ac_bc_in_id_effnrt)%rval elongf_stem = this%bc_in(ac_bc_in_id_efstem)%rval !--- Set some logical flags to simplify "if" blocks - is_hydecid_dormant = any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid] ) & + is_hydecid_dormant = any( prt_params%phen_leaf_habit(ipft) == [ihard_stress_decid,isemi_stress_decid] ) & .and. any(leaf_status == [leaves_off,leaves_shedding] ) - is_deciduous = any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid] ) & - .or. ( prt_params%season_decid(ipft) == itrue ) + is_deciduous = & + any( prt_params%phen_leaf_habit(ipft) == [ihard_season_decid,ihard_stress_decid,isemi_stress_decid] ) nleafage = prt_global%state_descriptor(leaf_c_id)%num_pos ! Number of leaf age class diff --git a/parteh/PRTGenericMod.F90 b/parteh/PRTGenericMod.F90 index e0a8c140fc..573a99cf3a 100644 --- a/parteh/PRTGenericMod.F90 +++ b/parteh/PRTGenericMod.F90 @@ -258,6 +258,7 @@ module PRTGenericMod procedure, non_overridable :: RegisterBCOut procedure, non_overridable :: RegisterBCInout procedure, non_overridable :: GetState + procedure, non_overridable :: GetBiomass procedure, non_overridable :: GetTurnover procedure, non_overridable :: GetBurned procedure, non_overridable :: GetHerbivory @@ -1056,6 +1057,36 @@ function GetState(this, organ_id, element_id, position_id) result(state_val) return end function GetState + ! ==================================================================================== + + subroutine GetBiomass(this, element_id, & + sapw_m, struct_m, leaf_m, fnrt_m, store_m, repro_m, alive_m, total_m) + + ! This subroutine returns the current amount of mass of a given element for all + ! organs, as well as some aggregate biomass variables. + + class(prt_vartypes) :: this + integer, intent(in) :: element_id ! Element type queried + real(r8), intent(out) :: sapw_m ! Sapwood mass (elemental, c, n, or p) [kg/plant] + real(r8), intent(out) :: struct_m ! Structural mass "" + real(r8), intent(out) :: leaf_m ! Leaf mass "" + real(r8), intent(out) :: fnrt_m ! Fineroot mass "" + real(r8), intent(out) :: store_m ! Storage mass "" + real(r8), intent(out) :: repro_m ! Total reproductive mass (on plant) "" + real(r8), intent(out) :: alive_m ! Alive biomass (sap+leaf+fineroot+repro+storage) "" + real(r8), intent(out) :: total_m ! Total vegetation mass "" + + sapw_m = this%GetState(sapw_organ, element_id) + struct_m = this%GetState(struct_organ, element_id) + leaf_m = this%GetState(leaf_organ, element_id) + fnrt_m = this%GetState(fnrt_organ, element_id) + store_m = this%GetState(store_organ, element_id) + repro_m = this%GetState(repro_organ, element_id) ! 2024-11-06: Is zero for now; include for future-proofing + + alive_m = leaf_m + fnrt_m + sapw_m + total_m = alive_m + store_m + struct_m + repro_m + + end subroutine GetBiomass ! ==================================================================================== diff --git a/parteh/PRTLossFluxesMod.F90 b/parteh/PRTLossFluxesMod.F90 index a3be88b044..6cca20182f 100644 --- a/parteh/PRTLossFluxesMod.F90 +++ b/parteh/PRTLossFluxesMod.F90 @@ -24,6 +24,7 @@ module PRTLossFluxesMod use FatesConstantsMod, only : nearzero use FatesConstantsMod, only : calloc_abs_error use FatesConstantsMod, only : itrue + use FatesConstantsMod, only : ievergreen use FatesGlobals , only : endrun => fates_endrun use FatesGlobals , only : fates_log use shr_log_mod , only : errMsg => shr_log_errMsg @@ -756,7 +757,7 @@ subroutine MaintTurnoverSimpleRetranslocation(prt,ipft,icanlayer,is_drought) ! Only evergreens have maintenance turnover (must also change trimming logic ! if we want to change this) ! ------------------------------------------------------------------------------------- - if ( leaf_long > nearzero .and. prt_params%evergreen(ipft)==itrue ) then + if ( leaf_long > nearzero .and. prt_params%phen_leaf_habit(ipft) == ievergreen ) then if(is_drought) then base_turnover(leaf_organ) = years_per_day / & diff --git a/parteh/PRTParametersMod.F90 b/parteh/PRTParametersMod.F90 index a4d6ddb3af..66202da6c5 100644 --- a/parteh/PRTParametersMod.F90 +++ b/parteh/PRTParametersMod.F90 @@ -11,23 +11,18 @@ module PRTParametersMod type,public :: prt_param_type - ! The following three PFT classes - ! are mutually exclusive - ! MLO: perhaps we should replace these three parameters with a single - ! parameter (phenology(:)) that is assigned different indices? - integer, allocatable :: stress_decid(:) ! Is the plant stress deciduous? - ! 0 - No - ! 1 - Drought "hard" deciduous (i.e., PFT - ! sheds leaves all at once when stressed) - ! 2 - Drought semi-deciduous (i.e., PFT - ! sheds leaves gradually as drought - ! conditions deteriorate) - integer, allocatable :: season_decid(:) ! Is the plant seasonally deciduous (1=yes, 0=no) - integer, allocatable :: evergreen(:) ! Is the plant an evergreen (1=yes, 0=no) - + integer, allocatable :: phen_leaf_habit(:) ! Leaf phenological habit? Current options include the following: + ! (actual values defined in FatesConstantsMod.F90) + ! - ievergreen - evergreen + ! - ihard_season_decid - obligate cold deciduous (i.e., + ! leaves will abscise and flush every winter) + ! - ihard_stress_decid - obligate drought deciduous (i.e., + ! leaves will abscie and flush at least once a year) + ! - isemi_stress_decid - drought semi-deciduous (i.e., + ! partial abscission and flushing are allowed). ! Drop fraction for tissues other than leaves (PFT-dependent) - real(r8), allocatable :: phen_fnrt_drop_fraction(:) ! Abscission fraction of fine roots - real(r8), allocatable :: phen_stem_drop_fraction(:) ! Abscission fraction of stems + real(r8), allocatable :: phen_fnrt_drop_fraction(:) ! Abscission fraction of fine roots + real(r8), allocatable :: phen_stem_drop_fraction(:) ! Abscission fraction of stems real(r8), allocatable :: phen_drought_threshold(:) ! For obligate (hard) drought deciduous, this is the threshold ! below which plants will abscise leaves, and ! above which plants will flush leaves. For semi-deciduous diff --git a/parteh/PRTParamsFATESMod.F90 b/parteh/PRTParamsFATESMod.F90 index 15eddb91b1..a668d6529d 100644 --- a/parteh/PRTParamsFATESMod.F90 +++ b/parteh/PRTParamsFATESMod.F90 @@ -4,7 +4,7 @@ module PRTInitParamsFatesMod ! the CLM/ELM module system. use FatesConstantsMod, only : r8 => fates_r8 - use FatesConstantsMod, only : itrue,ifalse + use FatesConstantsMod, only : itrue use FatesConstantsMod, only : nearzero use FatesConstantsMod, only : years_per_day use FatesInterfaceTypesMod, only : hlm_parteh_mode @@ -32,7 +32,8 @@ module PRTInitParamsFatesMod use FatesAllometryMod, only : set_root_fraction use PRTGenericMod, only : StorageNutrientTarget use EDTypesMod, only : init_recruit_trim - use FatesConstantsMod, only : ihard_stress_decid, isemi_stress_decid + use FatesConstantsMod, only : ievergreen + use FatesConstantsMod, only : isemi_stress_decid ! ! !PUBLIC TYPES: @@ -153,15 +154,7 @@ subroutine PRTRegisterPFT(fates_params) character(len=param_string_length) :: name - name = 'fates_phen_stress_decid' - call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & - dimension_names=dim_names, lower_bounds=dim_lower_bound) - - name = 'fates_phen_season_decid' - call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & - dimension_names=dim_names, lower_bounds=dim_lower_bound) - - name = 'fates_phen_evergreen' + name = 'fates_phen_leaf_habit' call fates_params%RegisterParameter(name=name, dimension_shape=dimension_shape_1d, & dimension_names=dim_names, lower_bounds=dim_lower_bound) @@ -452,25 +445,11 @@ subroutine PRTReceivePFT(fates_params) real(r8), allocatable :: tmpreal(:) ! Temporary variable to hold floats ! that are converted to ints - name = 'fates_phen_stress_decid' - call fates_params%RetrieveParameterAllocate(name=name, & - data=tmpreal) - allocate(prt_params%stress_decid(size(tmpreal,dim=1))) - call ArrayNint(tmpreal,prt_params%stress_decid) - deallocate(tmpreal) - - name = 'fates_phen_season_decid' - call fates_params%RetrieveParameterAllocate(name=name, & - data=tmpreal) - allocate(prt_params%season_decid(size(tmpreal,dim=1))) - call ArrayNint(tmpreal,prt_params%season_decid) - deallocate(tmpreal) - - name = 'fates_phen_evergreen' + name = 'fates_phen_leaf_habit' call fates_params%RetrieveParameterAllocate(name=name, & data=tmpreal) - allocate(prt_params%evergreen(size(tmpreal,dim=1))) - call ArrayNint(tmpreal,prt_params%evergreen) + allocate(prt_params%phen_leaf_habit(size(tmpreal,dim=1))) + call ArrayNint(tmpreal,prt_params%phen_leaf_habit) deallocate(tmpreal) name = 'fates_phen_stem_drop_fraction' @@ -990,9 +969,7 @@ subroutine FatesReportPFTParams(is_master) end if write(fates_log(),*) '----------- FATES PARTEH Parameters -----------------' - write(fates_log(),fmti) 'stress_decid = ',prt_params%stress_decid - write(fates_log(),fmti) 'season_decid = ',prt_params%season_decid - write(fates_log(),fmti) 'evergreen = ',prt_params%evergreen + write(fates_log(),fmti) 'phen_leaf_habit = ',prt_params%phen_leaf_habit write(fates_log(),fmt0) 'phen_fnrt_drop_fraction = ',prt_params%phen_fnrt_drop_fraction write(fates_log(),fmt0) 'phen_stem_drop_fraction = ',prt_params%phen_stem_drop_fraction write(fates_log(),fmt0) 'phen_doff_time = ',prt_params%phen_doff_time @@ -1083,7 +1060,7 @@ subroutine PRTDerivedParams() integer :: i, io ! generic loop index and organ loop index norgans = size(prt_params%organ_id,1) - npft = size(prt_params%evergreen,1) + npft = size(prt_params%phen_leaf_habit,1) ! Set the reverse lookup map for organs to the parameter file index allocate(prt_params%organ_param_id(num_organ_types)) @@ -1107,9 +1084,8 @@ subroutine PRTCheckParams(is_master) ! This subroutine performs logical checks on user supplied parameters. It cross ! compares various parameters and will fail if they don't make sense. ! Examples: - ! A tree can not be defined as both evergreen and deciduous. A woody plant - ! cannot have a structural biomass allometry intercept of 0, and a non-woody - ! plant (grass) can't have a non-zero intercept... + ! A woody plant cannot have a structural biomass allometry intercept of 0, and a + ! non-woody plant (grass) can't have a non-zero intercept... ! ----------------------------------------------------------------------------------- @@ -1124,10 +1100,6 @@ subroutine PRTCheckParams(is_master) integer :: iage ! leaf age class index integer :: norgans ! size of the plant organ dimension integer :: i, io ! generic loop index and organ loop index - logical :: is_evergreen ! Is the PFT evergreen - logical :: is_season_decid ! Is the PFT cold-deciduous? - logical :: is_stress_decid ! Is the PFT drought-deciduous? - logical :: is_semi_decid ! Is the PFT drought semi-deciduous? logical :: is_hmode_fine ! Did the height allometry pass the check? integer :: nerror ! Count number of errors. If this is not ! zero by theend of the subroutine, stop @@ -1137,7 +1109,7 @@ subroutine PRTCheckParams(is_master) - npft = size(prt_params%evergreen,1) + npft = size(prt_params%phen_leaf_habit,1) ! Prior to performing checks copy grperc to the ! organ dimensioned version @@ -1218,38 +1190,10 @@ subroutine PRTCheckParams(is_master) pftloop: do ipft = 1,npft - ! Check to see if evergreen, deciduous flags are mutually exclusive - ! By the way, if these are mutually exclusive, shouldn't we define a - ! single prt_params%leaf_phenology and a list of codes for the different - ! types (i.e., ievergreen, iseason_decid, istress_hard, istress_semi, etc.)? - ! ---------------------------------------------------------------------------------- - is_evergreen = prt_params%evergreen(ipft) == itrue - is_season_decid = prt_params%season_decid(ipft) == itrue - is_stress_decid = any(prt_params%stress_decid(ipft) == [ihard_stress_decid,isemi_stress_decid]) - is_semi_decid = prt_params%stress_decid(ipft) == isemi_stress_decid - - if ( ( is_evergreen .and. is_season_decid ) .or. & - ( is_evergreen .and. is_stress_decid ) .or. & - ( is_season_decid .and. is_stress_decid ) ) then - - write(fates_log(),*) '---~---' - write(fates_log(),*) 'PFT # ',ipft,' must be defined as having one of three' - write(fates_log(),*) 'phenology habits, ie, only one of the flags below should' - write(fates_log(),*) 'be different than ',ifalse - write(fates_log(),*) 'stress_decid: ',prt_params%stress_decid(ipft) - write(fates_log(),*) 'season_decid: ',prt_params%season_decid(ipft) - write(fates_log(),*) 'evergreen: ',prt_params%evergreen(ipft) - write(fates_log(),*) '---~---' - write(fates_log(),*) '' - write(fates_log(),*) '' - nerror = nerror + 1 - end if - - ! When using the the drought semi-deciduous phenology, we must ensure that the lower ! and upper thresholds are consistent (i.e., that both are based on either soil ! water content or soil matric potential). - if (is_semi_decid) then + if (prt_params%phen_leaf_habit(ipft) == isemi_stress_decid) then if ( prt_params%phen_drought_threshold(ipft)*prt_params%phen_moist_threshold(ipft) < 0._r8 ) then ! In case the product of the lower and upper thresholds is negative, the ! thresholds are inconsistent as both should be defined using the same @@ -1260,7 +1204,7 @@ subroutine PRTCheckParams(is_master) write(fates_log(),*) ' the dry threshold. Positive = soil water content [m3/m3],' write(fates_log(),*) ' Negative = soil matric potential [mm].' write(fates_log(),*) ' PFT = ',ipft - write(fates_log(),*) ' Stress_decid = ',prt_params%stress_decid(ipft) + write(fates_log(),*) ' phen_leaf_habit = ',prt_params%phen_leaf_habit(ipft) write(fates_log(),*) ' fates_phen_drought_threshold = ',prt_params%phen_drought_threshold(ipft) write(fates_log(),*) ' fates_phen_moist_threshold = ',prt_params%phen_moist_threshold (ipft) write(fates_log(),*) '---~---' @@ -1275,7 +1219,7 @@ subroutine PRTCheckParams(is_master) write(fates_log(),*) ' By greater we mean more positive or less negative, and' write(fates_log(),*) ' they cannot be the identical.' write(fates_log(),*) ' PFT = ',ipft - write(fates_log(),*) ' Stress_decid = ',prt_params%stress_decid(ipft) + write(fates_log(),*) ' phen_leaf_habit = ',prt_params%phen_leaf_habit(ipft) write(fates_log(),*) ' fates_phen_drought_threshold = ',prt_params%phen_drought_threshold(ipft) write(fates_log(),*) ' fates_phen_moist_threshold = ',prt_params%phen_moist_threshold (ipft) write(fates_log(),*) '---~---' @@ -1286,7 +1230,7 @@ subroutine PRTCheckParams(is_master) end if ! For all deciduous PFTs, check that abscission fractions are all bounded. - if (prt_params%evergreen(ipft) == ifalse) then + if (prt_params%phen_leaf_habit(ipft) /= ievergreen) then ! Check if the fraction of fine roots to be actively abscised relative to leaf abscission ! is bounded between 0 and 1 (exactly 0 and 1 are acceptable). if ( ( prt_params%phen_fnrt_drop_fraction(ipft) < 0.0_r8 ) .or. & @@ -1295,7 +1239,7 @@ subroutine PRTCheckParams(is_master) write(fates_log(),*) ' Abscission rate for fine roots must be between 0 and 1 for ' write(fates_log(),*) ' deciduous PFTs.' write(fates_log(),*) ' PFT#: ',ipft - write(fates_log(),*) ' evergreen flag: (should be 0):',prt_params%evergreen(ipft) + write(fates_log(),*) ' phen_leaf_habit: ',prt_params%phen_leaf_habit(ipft) write(fates_log(),*) ' phen_fnrt_drop_fraction: ', prt_params%phen_fnrt_drop_fraction(ipft) write(fates_log(),*) '---~---' write(fates_log(),*) '' @@ -1326,7 +1270,7 @@ subroutine PRTCheckParams(is_master) write(fates_log(),*) ' Deciduous non-wood plants must keep 0-100% of their stems' write(fates_log(),*) ' during the deciduous period.' write(fates_log(),*) ' PFT#: ',ipft - write(fates_log(),*) ' evergreen flag: (should be 0):',prt_params%evergreen(ipft) + write(fates_log(),*) ' phen_leaf_habit: ',prt_params%phen_leaf_habit(ipft) write(fates_log(),*) ' phen_stem_drop_fraction: ', prt_params%phen_stem_drop_fraction(ipft) write(fates_log(),*) '---~---' write(fates_log(),*) '' @@ -1793,20 +1737,19 @@ subroutine PRTCheckParams(is_master) nerror = nerror + 1 end if - else - if (prt_params%evergreen(ipft) .eq. itrue) then - write(fates_log(),*) "---~---" - write(fates_log(),*) 'You specified zero leaf turnover: ' - write(fates_log(),*) 'ipft: ',ipft,' iage: ',iage - write(fates_log(),*) 'leaf_long(ipft,iage): ',prt_params%leaf_long(ipft,iage) - write(fates_log(),*) 'yet this is an evergreen PFT, and it only makes sense' - write(fates_log(),*) 'that an evergreen would have leaf maintenance turnover' - write(fates_log(),*) 'disable this error if you are ok with this' - write(fates_log(),*) "---~---" - write(fates_log(),*) '' - write(fates_log(),*) '' - nerror = nerror + 1 - end if + elseif (prt_params%phen_leaf_habit(ipft) == ievergreen) then + write(fates_log(),*) "---~---" + write(fates_log(),*) 'You specified zero leaf turnover: ' + write(fates_log(),*) 'ipft: ',ipft,' iage: ',iage + write(fates_log(),*) 'phen_leaf_habit: ',prt_params%phen_leaf_habit(ipft) + write(fates_log(),*) 'leaf_long(ipft,iage): ',prt_params%leaf_long(ipft,iage) + write(fates_log(),*) 'yet this is an evergreen PFT, and it only makes sense' + write(fates_log(),*) 'that an evergreen would have leaf maintenance turnover' + write(fates_log(),*) 'disable this error if you are ok with this' + write(fates_log(),*) "---~---" + write(fates_log(),*) '' + write(fates_log(),*) '' + nerror = nerror + 1 end if end do @@ -1858,21 +1801,19 @@ subroutine PRTCheckParams(is_master) write(fates_log(),*) '' nerror = nerror + 1 end if - - else - if (prt_params%evergreen(ipft) .eq. itrue) then - write(fates_log(),*) "---~---" - write(fates_log(),*) 'You specified zero root turnover: ' - write(fates_log(),*) 'ipft: ',ipft - write(fates_log(),*) 'root_long(ipft): ',prt_params%root_long(ipft) - write(fates_log(),*) 'yet this is an evergreen PFT, and it only makes sense' - write(fates_log(),*) 'that an evergreen would have root maintenance turnover' - write(fates_log(),*) 'disable this error if you are ok with this' - write(fates_log(),*) "---~---" - write(fates_log(),*) '' - write(fates_log(),*) '' - nerror = nerror + 1 - end if + elseif (prt_params%phen_leaf_habit(ipft) == ievergreen) then + write(fates_log(),*) "---~---" + write(fates_log(),*) 'You specified zero root turnover: ' + write(fates_log(),*) 'ipft: ',ipft + write(fates_log(),*) 'phen_leaf_habit: ',prt_params%phen_leaf_habit(ipft) + write(fates_log(),*) 'root_long(ipft): ',prt_params%root_long(ipft) + write(fates_log(),*) 'yet this is an evergreen PFT, and it only makes sense' + write(fates_log(),*) 'that an evergreen would have root maintenance turnover' + write(fates_log(),*) 'disable this error if you are ok with this' + write(fates_log(),*) "---~---" + write(fates_log(),*) '' + write(fates_log(),*) '' + nerror = nerror + 1 end if ! Check Branch turnover doesn't exceed one day diff --git a/radiation/FatesRadiationDriveMod.F90 b/radiation/FatesRadiationDriveMod.F90 index d65c852219..ae5e464d3b 100644 --- a/radiation/FatesRadiationDriveMod.F90 +++ b/radiation/FatesRadiationDriveMod.F90 @@ -13,6 +13,7 @@ module FatesRadiationDriveMod use EDTypesMod , only : ed_site_type use FatesPatchMod, only : fates_patch_type use EDParamsMod, only : maxpft + use EDParamsMod , only : GetNVegLayers use FatesConstantsMod , only : r8 => fates_r8 use FatesConstantsMod , only : fates_unset_r8 use FatesConstantsMod , only : itrue @@ -373,11 +374,18 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) if_notair: if (ft>0) then area_frac = twostr%scelg(cl,icol)%area vai = twostr%scelg(cl,icol)%sai+twostr%scelg(cl,icol)%lai - nv = minloc(dlower_vai, DIM=1, MASK=(dlower_vai>vai)) + + nv = GetNVegLayers(vai) + do iv = 1, nv - vai_top = dlower_vai(iv)-dinc_vai(iv) - vai_bot = min(dlower_vai(iv),twostr%scelg(cl,icol)%sai+twostr%scelg(cl,icol)%lai) + vai_top = dlower_vai(iv) + + if(iv == nv) then + vai_bot = twostr%scelg(cl,icol)%sai+twostr%scelg(cl,icol)%lai + else + vai_bot = dlower_vai(iv+1) + end if call twostr%GetAbsRad(cl,icol,ipar,vai_top,vai_bot, & Rb_abs,Rd_abs,Rd_abs_leaf,Rb_abs_leaf,R_abs_stem,R_abs_snow,leaf_sun_frac,call_fail) diff --git a/radiation/TwoStreamMLPEMod.F90 b/radiation/TwoStreamMLPEMod.F90 index 0c6d5c397f..22bf303649 100644 --- a/radiation/TwoStreamMLPEMod.F90 +++ b/radiation/TwoStreamMLPEMod.F90 @@ -468,7 +468,8 @@ subroutine GetAbsRad(this,ican,icol,ib,vai_top,vai_bot, & real(r8), intent(out) :: leaf_sun_frac ! Fraction of leaves in the interval exposed ! to sunlight logical, intent(out) :: call_fail - real(r8) :: dvai,dlai ! Amount of VAI and LAI in this interval [m2/m2] + real(r8) :: dvai ! Amount of VAI in this interval [m2/m2] + real(r8) :: leaf_frac ! Fraction of leaf+stem that is leaf real(r8) :: Rd_net ! Difference in diffuse radiation at upper and lower boundaries [W/m2] real(r8) :: Rb_net ! Difference in beam radiation at upper and lower boundaries [W/m2] real(r8) :: vai_max ! total integrated (leaf+stem) area index of the current element @@ -502,29 +503,16 @@ subroutine GetAbsRad(this,ican,icol,ib,vai_top,vai_bot, & vai_max = scelg%lai + scelg%sai dvai = vai_bot - vai_top - - lai_top = vai_top*scelg%lai/( scelg%lai+ scelg%sai) - lai_bot = vai_bot*scelg%lai/( scelg%lai+ scelg%sai) - dlai = dvai * scelg%lai/( scelg%lai+ scelg%sai) - + leaf_frac = scelg%lai/( scelg%lai+ scelg%sai) + lai_top = vai_top*leaf_frac + lai_bot = vai_bot*leaf_frac - if(dlai>nearzero)then - leaf_sun_frac = max(0.001_r8,min(0.999_r8,scelb%Rbeam0/(dlai*scelg%Kb_leaf/rad_params%clumping_index(ft)) & - *(exp(-scelg%Kb_leaf*lai_top) - exp(-scelg%Kb_leaf*lai_bot)))) + if(dvai>nearzero)then + leaf_sun_frac = max(0.001_r8,min(0.999_r8, & + scelb%Rbeam0*(exp(-scelg%Kb*vai_top) - exp(-scelg%Kb*vai_bot))/(dvai*scelg%Kb))) else leaf_sun_frac = 0001._r8 end if - - !leaf_sun_frac = max(0.001_r8,min(0.999_r8,scelb%Rbeam0/(dvai*scelg%Kb/rad_params%clumping_index(ft)) & - ! *(exp(-scelg%Kb*vai_top) - exp(-scelg%Kb*vai_bot)))) - - - if(debug) then - if(leaf_sun_frac>1.0_r8 .or. leaf_sun_frac<0._r8) then - write(log_unit,*)"impossible leaf sun fraction" - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if ! We have to disentangle the absorption between leaves and stems, we give them both ! a weighting fraction of total absorption of area*K*(1-om) @@ -568,7 +556,7 @@ subroutine GetAbsRad(this,ican,icol,ib,vai_top,vai_bot, & r_dn_top = this%GetRdDn(ican,icol,ib,vai_top) r_dn_bot = this%GetRdDn(ican,icol,ib,vai_bot) - + if(r_dn_top<-1.e5 .or. r_dn_bot<-1.e5) then write(log_unit,*) 'error in diffuse calculations, negative values' call_fail = .true. diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt index dc2c79ee7f..3b788d51fa 100644 --- a/testing/CMakeLists.txt +++ b/testing/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(functional_testing/math_utils fates_math_ftest) add_subdirectory(functional_testing/fire/fuel fates_fuel_ftest) add_subdirectory(functional_testing/fire/ros fates_ros_ftest) add_subdirectory(functional_testing/patch fates_patch_ftest) +add_subdirectory(functional_testing/fire/mortality fates_firemort_ftest) ## Unit tests add_subdirectory(unit_testing/fire_weather_test fates_fire_weather_utest) diff --git a/testing/cime_setup.md b/testing/cime_setup.md index 8fd9148b59..93ec31aa42 100644 --- a/testing/cime_setup.md +++ b/testing/cime_setup.md @@ -72,9 +72,6 @@ Next set up some other environment variables: ```bash export ESMF_INSTALL_PREFIX=$ESMF_DIR/install_dir -export ESMF_NETCDF=split -export ESMF_NETCDF_INCLUDE=/usr/local/include -export ESMF_NETCDF_LIBPATH=/usr/local/lib export ESMF_COMM=openmpi export ESMF_COMPILER=gfortranclang ``` diff --git a/testing/functional_class_with_drivers.py b/testing/functional_class_with_drivers.py new file mode 100644 index 0000000000..8a0b3ae0b3 --- /dev/null +++ b/testing/functional_class_with_drivers.py @@ -0,0 +1,15 @@ +import os +from functional_class import FunctionalTest + + +class FunctionalTestWithDrivers(FunctionalTest): + """Class for running FATES functional tests with driver files""" + + def __init__(self, datm_file: str, *args): + + # Check that datm exists and save its absolute path + self.datm_file = os.path.abspath(datm_file) + if not os.path.exists(self.datm_file): + raise FileNotFoundError(f"datm_file not found: '{self.datm_file}'") + + super().__init__(*args) diff --git a/testing/functional_testing/allometry/allometry_test.py b/testing/functional_testing/allometry/allometry_test.py index bb24ab3729..4b070bc8db 100644 --- a/testing/functional_testing/allometry/allometry_test.py +++ b/testing/functional_testing/allometry/allometry_test.py @@ -6,7 +6,8 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from utils import round_up, get_color_palette, blank_plot +from utils import round_up +from utils_plotting import blank_plot, get_color_palette from functional_class import FunctionalTest diff --git a/testing/functional_testing/fire/fuel/fuel_test.py b/testing/functional_testing/fire/fuel/fuel_test.py index b9cd151621..9ae6c53e39 100644 --- a/testing/functional_testing/fire/fuel/fuel_test.py +++ b/testing/functional_testing/fire/fuel/fuel_test.py @@ -5,16 +5,17 @@ import numpy as np import xarray as xr import matplotlib.pyplot as plt -from functional_class import FunctionalTest +from functional_class_with_drivers import FunctionalTestWithDrivers -class FuelTest(FunctionalTest): +class FuelTest(FunctionalTestWithDrivers): """Fuel test class""" name = "fuel" def __init__(self, test_dict): super().__init__( + test_dict["datm_file"], FuelTest.name, test_dict["test_dir"], test_dict["test_exe"], diff --git a/testing/functional_testing/fire/mortality/CMakeLists.txt b/testing/functional_testing/fire/mortality/CMakeLists.txt new file mode 100644 index 0000000000..f9f9ca5d35 --- /dev/null +++ b/testing/functional_testing/fire/mortality/CMakeLists.txt @@ -0,0 +1,24 @@ +set(fire_mortality_test_sources + FatesTestFireMortality.F90) + +set(NETCDF_C_DIR ${NETCDF_C_PATH}) +set(NETCDF_FORTRAN_DIR ${NETCDF_F_PATH}) + +FIND_PATH(NETCDFC_FOUND libnetcdf.a ${NETCDF_C_DIR}/lib) +FIND_PATH(NETCDFF_FOUND libnetcdff.a ${NETCDF_FORTRAN_DIR}/lib) + +include_directories(${NETCDF_C_DIR}/include + ${NETCDF_FORTRAN_DIR}/include) + +link_directories(${NETCDF_C_DIR}/lib + ${NETCDF_FORTRAN_DIR}/lib + ${PFUNIT_TOP_DIR}/lib) + +add_executable(FATES_firemort_exe ${fire_mortality_test_sources}) + +target_link_libraries(FATES_firemort_exe + netcdf + netcdff + fates + csm_share + funit) \ No newline at end of file diff --git a/testing/functional_testing/fire/mortality/FatesTestFireMortality.F90 b/testing/functional_testing/fire/mortality/FatesTestFireMortality.F90 new file mode 100644 index 0000000000..02f973ea39 --- /dev/null +++ b/testing/functional_testing/fire/mortality/FatesTestFireMortality.F90 @@ -0,0 +1,456 @@ +program FatesTestFireMortality + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesArgumentUtils, only : command_line_arg + use FatesUnitTestParamReaderMod, only : fates_unit_test_param_reader + use PRTParametersMod, only : prt_params + + implicit none + + ! LOCALS: + type(fates_unit_test_param_reader) :: param_reader ! param reader instance + character(len=:), allocatable :: param_file ! input parameter file + real(r8), allocatable :: tree_diameter(:) ! tree diameter at breast height [cm] + real(r8), allocatable :: tau_c(:,:) ! critical residence time for cambial death [min] + real(r8), allocatable :: mortality(:,:,:) ! probability of fire mortality + real(r8), allocatable :: crown_kill(:) ! fraction of crown volume burned + real(r8), allocatable :: tau_r_out(:) ! relative fire residence time + real(r8), allocatable :: fire_mortality_bySH(:, :) ! total fire mortality probability (by SH) + real(r8), allocatable :: fire_mortality_bytau(:, :) ! total fire mortality probability (by tau) + real(r8), allocatable :: SH(:) ! scorch height [m] + real(r8), allocatable :: tau_l(:) ! residence time of fire [min] + integer :: num_pfts ! number of pfts (from parameter files) + + ! CONSTANTS: + character(len=*), parameter :: out_file = 'fire_mortality_out.nc' ! output file + + interface + + subroutine TestTauC(num_pfts, tree_diameter, tau_c) + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : itrue + use SFEquationsMod, only : CriticalResidenceTime, BarkThickness + use EDPftvarcon, only : EDPftvarcon_inst + use PRTParametersMod, only : prt_params + implicit none + integer, intent(in) :: num_pfts + real(r8), allocatable, intent(out) :: tree_diameter(:) + real(r8), allocatable, intent(out) :: tau_c(:,:) + + end subroutine TestTauC + + subroutine TestMortalityProb(num_pfts, mortality, crown_kill, tau_r_out) + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : itrue + use SFEquationsMod, only : CriticalResidenceTime, BarkThickness + use SFEquationsMod, only : TotalFireMortality, CrownFireMortality + use SFEquationsMod, only : cambial_mort + use EDPftvarcon, only : EDPftvarcon_inst + use PRTParametersMod, only : prt_params + implicit none + integer, intent(in) :: num_pfts + real(r8), allocatable, intent(out) :: mortality(:,:,:) + real(r8), allocatable, intent(out) :: crown_kill(:) + real(r8), allocatable, intent(out) :: tau_r_out(:) + end subroutine TestMortalityProb + + subroutine TestFireMortalitySensitivity(fire_mortality_bySH, fire_mortality_bytau, & + SH_out, tau_l_out) + use FatesConstantsMod, only : r8 => fates_r8 + use SFEquationsMod, only : CrownFractionBurnt, CambialMortality + use SFEquationsMod, only : CrownFireMortality, TotalFireMortality + implicit none + real(r8), allocatable, intent(out) :: fire_mortality_bySH(:,:) + real(r8), allocatable, intent(out) :: fire_mortality_bytau(:,:) + real(r8), allocatable, intent(out) :: SH_out(:) + real(r8), allocatable, intent(out) :: tau_l_out(:) + end subroutine TestFireMortalitySensitivity + + subroutine WriteFireMortData(out_file, num_pfts, tree_diameter, tau_c, mortality, & + crown_kill, tau_r_out, fire_mortality_bySH, SH, fire_mortality_bytau, tau_l) + + use FatesConstantsMod, only : r8 => fates_r8 + use FatesUnitTestIOMod, only : OpenNCFile, CloseNCFile, RegisterNCDims + use FatesUnitTestIOMod, only : RegisterVar, EndNCDef, WriteVar + use FatesUnitTestIOMod, only : type_double + implicit none + character(len=*), intent(in) :: out_file + integer, intent(in) :: num_pfts + real(r8), intent(in) :: tree_diameter(:) + real(r8), intent(in) :: tau_c(:,:) + real(r8), intent(in) :: mortality(:,:,:) + real(r8), intent(in) :: crown_kill(:) + real(r8), intent(in) :: tau_r_out(:) + real(r8), intent(in) :: fire_mortality_bySH(:,:) + real(r8), intent(in) :: SH(:) + real(r8), intent(in) :: fire_mortality_bytau(:,:) + real(r8), intent(in) :: tau_l(:) + + end subroutine WriteFireMortData + + end interface + + ! read in parameter file name and DATM file from command line + param_file = command_line_arg(1) + + ! read in parameter file + call param_reader%Init(param_file) + call param_reader%RetrieveParameters() + num_pfts = size(prt_params%wood_density, dim=1) + + ! calculate propagating flux + call TestTauC(num_pfts, tree_diameter, tau_c) + + ! calculate total fire mortality + call TestMortalityProb(num_pfts, mortality, crown_kill, tau_r_out) + + ! test sensitivity to SH and cambial kill + call TestFireMortalitySensitivity(fire_mortality_bySH, fire_mortality_bytau, SH, tau_l) + + ! write output data + call WriteFireMortData(out_file, num_pfts, tree_diameter, tau_c, mortality, & + crown_kill, tau_r_out, fire_mortality_bySH, SH, fire_mortality_bytau, tau_l) + + ! deallocate arrays + if (allocated(tree_diameter)) deallocate(tree_diameter) + if (allocated(tau_c)) deallocate(tau_c) + if (allocated(mortality)) deallocate(mortality) + if (allocated(crown_kill)) deallocate(crown_kill) + if (allocated(tau_r_out)) deallocate(tau_r_out) + if (allocated(fire_mortality_bySH)) deallocate(fire_mortality_bySH) + if (allocated(fire_mortality_bytau)) deallocate(fire_mortality_bytau) + if (allocated(SH)) deallocate(SH) + if (allocated(tau_l)) deallocate(tau_l) + +end program FatesTestFireMortality + +!========================================================================================= + +subroutine TestFireMortalitySensitivity(fire_mortality_bySH, fire_mortality_bytau, SH_out, & + tau_l_out) + ! + ! DESCRIPTION: + ! Calculates fire mortality for some input tree characteristics and fire behaviors - based + ! on scorch height + ! + use FatesConstantsMod, only : r8 => fates_r8 + use SFEquationsMod, only : CrownFractionBurnt, CambialMortality + use SFEquationsMod, only : CrownFireMortality, TotalFireMortality + + implicit none + + ! ARGUMENTS: + real(r8), allocatable, intent(out) :: fire_mortality_bySH(:,:) ! total fire mortality (by SH) + real(r8), allocatable, intent(out) :: fire_mortality_bytau(:,:) ! total fire mortality (by tau) + real(r8), allocatable, intent(out) :: SH_out(:) ! scorch height of tree [m] + real(r8), allocatable, intent(out) :: tau_l_out(:) ! residence times of fire [min] + + ! LOCALS: + integer :: i, j ! looping indices + real(r8) :: fraction_crown_burned ! fraction of the crown burned + real(r8) :: cambial_mort ! cambial mortality + real(r8) :: bark_scaler ! cm bark per cm dbh + real(r8) :: crownfire_mort ! crown fire mortality + + ! CONSTANTS: + real(r8), parameter, dimension(3) :: SH = (/5.0_r8, 10.0_r8, 20.0_r8/) ! scorch heights of fire [m] + real(r8), parameter, dimension(3) :: tau_l = (/2.4_r8, 3.1_r8, 6.0_r8/) ! residence times of fire [min] + real(r8), parameter, dimension(6) :: dbh = (/20.0_r8, 40.0_r8, 20.0_r8, 40.0_r8, 20.0_r8, 40.0_r8/) ! diameters of trees [cm] + real(r8), parameter, dimension(6) :: crown_length = (/12.0_r8, 18.5_r8, 13.5_r8, 23.1_r8, 11.9_r8, 18.2_r8/) ! crown depth of trees [m] + real(r8), parameter, dimension(6) :: bark_thickness = (/1.1_r8, 2.2_r8, 0.9_r8, 1.7_r8, 0.3_r8, 0.6_r8/) ! bark thickness of trees [cm] + real(r8), parameter, dimension(6) :: height = (/15.5_r8, 24.4_r8, 17.4_r8, 30.7_r8, 15.1_r8, 24.1_r8/) ! height of trees [m] + real(r8), parameter :: crown_kill_parameter = 0.775_r8 ! parameter for crown kill + + allocate(fire_mortality_bySH(size(SH), size(dbh))) + allocate(fire_mortality_bytau(size(tau_l), size(dbh))) + allocate(SH_out(size(SH))) + allocate(tau_l_out(size(tau_l))) + + do i = 1, size(SH) + SH_out(i) = SH(i) + do j = 1, size(dbh) + bark_scaler = bark_thickness(j)/dbh(j) + fraction_crown_burned = CrownFractionBurnt(SH(i), height(j), crown_length(j)) + cambial_mort = CambialMortality(bark_scaler, dbh(j), 6.0_r8) + crownfire_mort = CrownFireMortality(crown_kill_parameter, fraction_crown_burned) + fire_mortality_bySH(i,j) = TotalFireMortality(crownfire_mort, cambial_mort) + end do + end do + + do i = 1, size(tau_l) + tau_l_out(i) = tau_l(i) + do j = 1, size(dbh) + bark_scaler = bark_thickness(j)/dbh(j) + fraction_crown_burned = CrownFractionBurnt(10.0_r8, height(j), crown_length(j)) + cambial_mort = CambialMortality(bark_scaler, dbh(j), tau_l(i)) + crownfire_mort = CrownFireMortality(crown_kill_parameter, fraction_crown_burned) + fire_mortality_bytau(i,j) = TotalFireMortality(crownfire_mort, cambial_mort) + end do + end do + +end subroutine TestFireMortalitySensitivity + +!========================================================================================= + +subroutine TestTauC(num_pfts, tree_diameter, tau_c) + ! + ! DESCRIPTION: + ! Calculates critical time for cambial kill over a range of diameter values and pfts + ! + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : itrue + use SFEquationsMod, only : CriticalResidenceTime, BarkThickness + use EDPftvarcon, only : EDPftvarcon_inst + use PRTParametersMod, only : prt_params + + implicit none + + ! ARGUMENTS: + integer, intent(in) :: num_pfts ! number of pfts + real(r8), allocatable, intent(out) :: tree_diameter(:) ! tree diameter at breast height [cm] + real(r8), allocatable, intent(out) :: tau_c(:,:) ! critical fire residence time for cambial kill [min] + + ! CONSTANTS: + real(r8), parameter :: dbh_min = 2.5_r8 ! minimum dbh to calculate [cm] + real(r8), parameter :: dbh_max = 60.0_r8 ! maximum dbh to calculate [cm] + real(r8), parameter :: dbh_inc = 1.0_r8 ! dbh increment to scale [cm] + + ! LOCALS: + real(r8) :: bark_thickness ! bark thickness [cm] + integer :: num_dbh ! size of dbh array + integer :: i, j ! looping indices + + ! allocate arrays + num_dbh = int((dbh_max - dbh_min)/dbh_inc + 1) + allocate(tree_diameter(num_dbh)) + allocate(tau_c(num_dbh, num_pfts)) + + do i = 1, num_dbh + + tree_diameter(i) = dbh_min + dbh_inc*(i-1) + + do j = 1, num_pfts + if (prt_params%woody(j) == itrue) then + bark_thickness = BarkThickness(EDPftvarcon_inst%bark_scaler(j), tree_diameter(i)) + tau_c(i,j) = CriticalResidenceTime(bark_thickness) + else + tau_c(i,j) = 0.0_r8 + end if + end do + end do + +end subroutine TestTauC + +!========================================================================================= + +subroutine TestMortalityProb(num_pfts, mortality, crown_kill, tau_r_out) + ! + ! DESCRIPTION: + ! Calculates mortality probability for a range of values of fraction crown killed and + ! relative residence time + ! + use FatesConstantsMod, only : r8 => fates_r8 + use FatesConstantsMod, only : itrue + use SFEquationsMod, only : CriticalResidenceTime, BarkThickness + use SFEquationsMod, only : TotalFireMortality, CrownFireMortality + use SFEquationsMod, only : cambial_mort + use EDPftvarcon, only : EDPftvarcon_inst + use PRTParametersMod, only : prt_params + + implicit none + + ! ARGUMENTS: + integer, intent(in) :: num_pfts ! number of pfts + real(r8), allocatable, intent(out) :: mortality(:,:,:) ! probability of fire mortality + real(r8), allocatable, intent(out) :: crown_kill(:) ! fraction of crown volume burned + real(r8), allocatable, intent(out) :: tau_r_out(:) ! relative fire residence times + + ! CONSTANTS: + real(r8), parameter :: mort_min = 0.0_r8 ! minimum mortality rate to calculate + real(r8), parameter :: mort_max = 1.0_r8 ! maximum mortality rate to calculate + real(r8), parameter :: mort_inc = 0.05_r8 ! mortality rates to scale + real(r8), parameter, dimension(5) :: tau_r = (/0.22_r8, 0.4_r8, 0.66_r8, 1.0_r8, 2.0_r8/) ! relative fire residence times + + ! LOCALS: + real(r8) :: crownfire_mort ! crown fire mortality + real(r8) :: cambial_damage_mort ! cambial damage mortality + integer :: num_mort ! size of dbh array + integer :: i, j, k ! looping indices + + ! allocate arrays + num_mort = int((mort_max - mort_min)/mort_inc + 1) + allocate(mortality(num_mort, num_pfts, size(tau_r))) + allocate(crown_kill(num_mort)) + allocate(tau_r_out(size(tau_r))) + + do i = 1, num_mort + + crown_kill(i) = mort_min + mort_inc*(i-1) + + do j = 1, num_pfts + do k = 1, size(tau_r) + tau_r_out(k) = tau_r(k) + + if (prt_params%woody(j) == itrue) then + cambial_damage_mort = cambial_mort(tau_r(k)) + crownfire_mort = CrownFireMortality(EDPftvarcon_inst%crown_kill(j), crown_kill(i)) + mortality(i,j,k) = TotalFireMortality(crownfire_mort, cambial_damage_mort) + else + mortality(i,j,k) = 0.0_r8 + end if + end do + end do + end do + +end subroutine TestMortalityProb + +!========================================================================================= + +subroutine WriteFireMortData(out_file, num_pfts, tree_diameter, tau_c, mortality, & + crown_kill, tau_r_out, fire_mortality_bySH, SH, fire_mortality_bytau, tau_l) + ! + ! DESCRIPTION: + ! writes out data from the test + ! + use FatesConstantsMod, only : r8 => fates_r8 + use FatesUnitTestIOMod, only : OpenNCFile, CloseNCFile, RegisterNCDims + use FatesUnitTestIOMod, only : RegisterVar, EndNCDef, WriteVar + use FatesUnitTestIOMod, only : type_double, type_int + + implicit none + + ! ARGUMENTS: + character(len=*), intent(in) :: out_file + integer, intent(in) :: num_pfts + real(r8), intent(in) :: tree_diameter(:) + real(r8), intent(in) :: tau_c(:,:) + real(r8), intent(in) :: mortality(:,:,:) + real(r8), intent(in) :: crown_kill(:) + real(r8), intent(in) :: tau_r_out(:) + real(r8), intent(in) :: fire_mortality_bySH(:,:) + real(r8), intent(in) :: SH(:) + real(r8), intent(in) :: fire_mortality_bytau(:,:) + real(r8), intent(in) :: tau_l(:) + + ! LOCALS: + integer, allocatable :: pft_indices(:) ! array of pft indices to write out + integer, allocatable :: tree_indices(:) ! array of tree indices to write out + integer :: ncid ! netcdf id + character(len=20) :: dim_names(7) ! dimension names + integer :: dimIDs(7) ! dimension IDs + integer :: i ! looping index + integer :: dbhID, pftID + integer :: taurID + integer :: taucID + integer :: mortID, crownkillID + integer :: SHID, firemortbySHID + integer :: treeID, firemortbytauID + integer :: taulID + + ! dimension names + dim_names = [character(len=20) :: 'dbh', 'pft', 'crown_kill', 'tau_r', 'SH', 'treeID', 'tau_l'] + + ! create pft indices + allocate(pft_indices(num_pfts)) + do i = 1, num_pfts + pft_indices(i) = i + end do + + ! create tree indices + allocate(tree_indices(size(fire_mortality_bySH, dim=2))) + do i = 1, size(fire_mortality_bySH, dim=2) + tree_indices(i) = i + end do + + ! open file + call OpenNCFile(trim(out_file), ncid, 'readwrite') + + ! register dimensions + call RegisterNCDims(ncid, dim_names, (/size(tree_diameter), num_pfts, size(crown_kill), & + size(tau_r_out), size(SH), size(tree_indices), size(tau_l)/), size(dim_names), dimIDs) + + ! first register dimension variables + + ! register dbh + call RegisterVar(ncid, dim_names(1), dimIDs(1:1), type_double, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: 'cm', 'diameter at breast height'], 2, dbhID) + + ! register pft + call RegisterVar(ncid, dim_names(2), dimIDs(2:2), type_int, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: '', 'plant functional type'], 2, pftID) + + ! register crown kill + call RegisterVar(ncid, dim_names(3), dimIDs(3:3), type_double, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: '', 'fraction crown volume burned'], 2, crownkillID) + + ! register tau_r + call RegisterVar(ncid, dim_names(4), dimIDs(4:4), type_double, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: '', 'relative fire residence time'], 2, taurID) + + ! register scorch height + call RegisterVar(ncid, dim_names(5), dimIDs(5:5), type_double, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: 'm', 'scorch height'], 2, SHID) + + ! register tree ids + call RegisterVar(ncid, dim_names(6), dimIDs(6:6), type_int, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: '', 'tree indices'], 2, treeID) + + ! register tau_l + call RegisterVar(ncid, dim_names(7), dimIDs(7:7), type_double, & + [character(len=20) :: 'units', 'long_name'], & + [character(len=150) :: 'min', 'residence time of fire'], 2, taulID) + + ! then register actual variables + + ! register tau_c + call RegisterVar(ncid, 'tau_c', dimIDs(1:2), type_double, & + [character(len=20) :: 'coordinates', 'units', 'long_name'], & + [character(len=150) :: 'pft dbh', 'min', 'critical residence time for cambial death'], & + 3, taucID) + + ! register total mortality + call RegisterVar(ncid, 'total_mortality', (/dimIDs(3), dimIDs(2), dimIDs(4)/), type_double, & + [character(len=20) :: 'coordinates', 'units', 'long_name'], & + [character(len=150) :: 'crown_kill pft tau_r', '', 'fire mortality'], & + 3, mortID) + + ! register total mortality + call RegisterVar(ncid, 'fire_mortality_bySH', (/dimIDs(5), dimIDs(6)/), type_double, & + [character(len=20) :: 'coordinates', 'units', 'long_name'], & + [character(len=150) :: 'SH treeID', '', 'fire mortality by SH'], & + 3, firemortbySHID) + + ! register total mortality + call RegisterVar(ncid, 'fire_mortality_bytau', (/dimIDs(7), dimIDs(6)/), type_double, & + [character(len=20) :: 'coordinates', 'units', 'long_name'], & + [character(len=150) :: 'tau_l treeID', '', 'fire mortality by tau'], & + 3, firemortbytauID) + + + ! finish defining variables + call EndNCDef(ncid) + + ! write out data + call WriteVar(ncid, dbhID, tree_diameter(:)) + call WriteVar(ncid, pftID, pft_indices(:)) + call WriteVar(ncid, crownkillID, crown_kill(:)) + call WriteVar(ncid, taurID, tau_r_out(:)) + call WriteVar(ncid, taucID, tau_c(:,:)) + call WriteVar(ncid, mortID, mortality(:,:,:)) + call WriteVar(ncid, SHID, SH(:)) + call WriteVar(ncid, taulID, tau_l(:)) + call WriteVar(ncid, treeID, tree_indices(:)) + call WriteVar(ncid, firemortbySHID, fire_mortality_bySH(:,:)) + call WriteVar(ncid, firemortbytauID, fire_mortality_bytau(:,:)) + + ! close file + call CloseNCFile(ncid) + +end subroutine WriteFireMortData \ No newline at end of file diff --git a/testing/functional_testing/fire/mortality/fire_mortality_test.py b/testing/functional_testing/fire/mortality/fire_mortality_test.py new file mode 100644 index 0000000000..58e6055d53 --- /dev/null +++ b/testing/functional_testing/fire/mortality/fire_mortality_test.py @@ -0,0 +1,203 @@ +""" +Concrete class for running the ros functional test for FATES. +""" +import os +import numpy as np +import xarray as xr +import pandas as pd +import matplotlib.pyplot as plt +from functional_class import FunctionalTest +from utils import blank_plot, get_color_palette + +class FireMortTest(FunctionalTest): + """Fire mortality test class""" + + name = "fire_mortality" + + def __init__(self, test_dict): + super().__init__( + FireMortTest.name, + test_dict["test_dir"], + test_dict["test_exe"], + test_dict["out_file"], + test_dict["use_param_file"], + test_dict["other_args"], + ) + self.plot = True + + def plot_output(self, run_dir: str, save_figs: bool, plot_dir: str): + """Plot output associated with fuel tests + + Args: + run_dir (str): run directory + out_file (str): output file + save_figs (bool): whether or not to save the figures + plot_dir (str): plot directory + """ + + # get output file + mortality_dat = xr.open_dataset(os.path.join(run_dir, self.out_file)) + + # observational datasets + obs_dat, sh_obs, tau_obs = self.get_obs_dfs() + + # plot observational comparisons + self.bar_plot(mortality_dat, obs_dat, sh_obs, 'SH', 'fire_mortality_bySH', + 'Scorch Height (m)', save_figs, plot_dir) + + self.bar_plot(mortality_dat, obs_dat, tau_obs, 'tau_l', 'fire_mortality_bytau', + 'Fire Residence Time (min)', save_figs, plot_dir) + + self.plot_tau_c(mortality_dat, obs_dat, save_figs, plot_dir) + + @staticmethod + def plot_tau_c(ds, obs_dat, save_figs, plot_dir): + + data_frame = pd.DataFrame({ + "dbh": np.tile(ds.dbh, len(ds.pft)), + "pft": np.repeat(ds.pft, len(ds.dbh)), + 'tau_c': ds.tau_c.values.flatten()}) + + max_dbh = data_frame["dbh"].max() + max_tau = data_frame["tau_c"].max() + + blank_plot(max_dbh, 0.0, max_tau, 0.0, draw_horizontal_lines=True) + + pfts = np.unique(data_frame.pft.values) + colors = get_color_palette(len(pfts)) + for rank, pft in enumerate(pfts): + dat = data_frame[data_frame.pft == pft] + plt.plot( + dat.dbh.values, + dat['tau_c'].values, + lw=2, + color=colors[rank], + label=pft, + ) + plt.scatter(obs_dat.diameter, obs_dat.tau_c, c='black', label='observations') + plt.xlabel("DBH (cm)", fontsize=11) + plt.ylabel("Critical Fire Residence Time for Cambial Burning (min)", fontsize=11) + plt.title("Critical fire resdience time", fontsize=11) + plt.legend(loc="upper left", title="PFT") + if save_figs: + fig_name = os.path.join(plot_dir, f"tauc_plot.png") + plt.savefig(fig_name) + + @staticmethod + def bar_plot(ds: xr.Dataset, tree_obs: pd.DataFrame, mortality_obs: pd.DataFrame, + xvar: str, mortality_var: str, xlab: str, save_figs: bool, plot_dir: str): + """Plots a bar plot based on input dataframe + + Args: + df (xr.Dataset): input dataset + tree_obs (pd.DataFrame): dataframe with observations about trees + mortality_obs (pd.DataFrame): dataframe with observations about tree mortality + xvar (str): variable to plot on x axis + mortality_var (str): mortality variable + xlab (str): xlabel + save_figs (bool): whether or not to save figures + plot_dir (str): where to save figures + """ + + data_frame = pd.DataFrame({ + "treeID": np.repeat(ds.treeID, len(ds[xvar])), + xvar: np.tile(ds[xvar], len(ds.treeID)), + 'fire_mortality': ds[mortality_var].values.flatten()}) + data_frame['type'] = 'modeled' + df = pd.concat([data_frame, mortality_obs]) + df = df.merge(tree_obs, on='treeID', how='inner') + + fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(12, 6), sharey=True) + axes = axes.flatten() + + bar_width = 0.3 + + # unique values for x-axis + x_values = np.unique(df[xvar]) + x = np.arange(len(x_values)) + + legend_handles = [] + + # loop over unique treeIDs and create subplots + for i, treeID in enumerate(np.unique(df.treeID)): + + # extract data for this tree + dat = df[df.treeID == treeID] + + # extract modeled and observed fire mortality + modeled = dat[dat['type'] == 'modeled'] + observed = dat[dat['type'] == 'observations'] + + # plot bars side-by-side + bars1 = axes[i].bar(x - bar_width/2, (modeled.fire_mortality)*100.0, + width=bar_width, label='Modeled', color='blue') + bars2 = axes[i].bar(x + bar_width/2, (observed.fire_mortality)*100.0, + width=bar_width, label='Observed', color='orange') + if i == 0: + legend_handles.extend([bars1[0], bars2[0]]) + + # for printing + tree_dbh = observed.diameter.values[0] + crown_length = observed.crown_length.values[0] + height = observed.height.values[0] + bark_thickness = observed.bark_thickness.values[0] + + # formatting + axes[i].set_title(f"dbh: {tree_dbh} cm\nheight: {height} m\ncrown length: {crown_length} m\nbark thickness: {bark_thickness} cm") + axes[i].set_xticks(x) + axes[i].set_xticklabels(x_values) + axes[i].set_xlabel(xlab) + axes[i].set_ylabel('Fire Mortality') + + fig.legend(legend_handles, ['Modeled', 'Observed'], loc='upper center', + bbox_to_anchor=(0.5, 1.05), ncol=2) + plt.tight_layout() + if save_figs: + fig_name = os.path.join(plot_dir, f"{xvar}_plot.png") + plt.savefig(fig_name) + + + @staticmethod + def get_obs_dfs(): + """Return some hard-coded observational datasets + Peterson & Ryan 1986 Environmental Management: 10(6) + + Returns: + tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: output plots + """ + + obs_dat = pd.DataFrame({'species': ['douglas-fir', 'douglas-fir', + 'grand fir', 'grand fir', + 'subalpine fir', 'subalpine fir'], + 'treeID': [1, 2, 3, 4, 5, 6], + 'diameter': [20.0, 40.0, 20.0, 40.0, 20.0, 40.0], + 'height': [15.5, 24.4, 17.4, 30.7, 15.1, 24.1], + 'crown_length': [12.0, 18.5, 13.5, 23.1, 11.9, 18.2], + 'bark_thickness': [1.1, 2.2, 0.9, 1.7, 0.3, 0.6], + 'tau_c': [3.2, 13.4, 2.1, 8.3, 0.3, 1.0]}) + + sh_obs = pd.DataFrame({'fire_mortality': [0.94, 0.0, 1.0, 0.0, 1.0, 1.0, + 0.99, 0.03, 1.0, 0.13, 1.0, 1.0, + 1.0, 0.72, 1.0, 0.71, 1.0, 1.0], + 'SH': [5.0, 5.0, 5.0, 5.0, 5.0, 5.0, + 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, + 20.0, 20.0, 20.0, 20.0, 20.0, 20.0], + 'treeID': [1, 2, 3, 4, 5, 6, + 1, 2, 3, 4, 5, 6, + 1, 2, 3, 4, 5, 6]}) + sh_obs['type'] = 'observations' + + tau_obs = pd.DataFrame({'fire_mortality': [0.74, 0.0, 0.82, 0.0, 1.0, 1.0, + 0.83, 0.0, 0.9, 0.01, 1.0, 1.0, + 0.99, 0.03, 1.0, 0.13, 1.0, 1.0], + 'tau_l': [2.4, 2.4, 2.4, 2.4, 2.4, 2.4, + 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, + 6, 6, 6, 6, 6, 6], + 'treeID': [1, 2, 3, 4, 5, 6, + 1, 2, 3, 4, 5, 6, + 1, 2, 3, 4, 5, 6]}) + tau_obs['type'] = 'observations' + + return obs_dat, sh_obs, tau_obs + + diff --git a/testing/functional_testing/fire/ros/ros_test.py b/testing/functional_testing/fire/ros/ros_test.py index e845040fdb..56c7469095 100644 --- a/testing/functional_testing/fire/ros/ros_test.py +++ b/testing/functional_testing/fire/ros/ros_test.py @@ -7,7 +7,7 @@ import pandas as pd import matplotlib.pyplot as plt from functional_class import FunctionalTest -from utils import blank_plot +from utils_plotting import blank_plot COLORS = ["#793922", "#6B8939", "#99291F", "#CC9728", "#2C778A"] CM_TO_FT = 30.48 diff --git a/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 b/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 index ce1c8e85e2..aab21bab41 100644 --- a/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 +++ b/testing/functional_testing/fire/shr/SyntheticFuelModels.F90 @@ -178,7 +178,7 @@ integer function FuelModelPosition(this, fuel_model_index) end if end do write(*, '(a, i2, a)') "Cannot find the fuel model index ", fuel_model_index, "." - stop + call abort() end function FuelModelPosition diff --git a/testing/functional_testing/math_utils/FatesTestMathUtils.F90 b/testing/functional_testing/math_utils/FatesTestMathUtils.F90 index 627eb2713b..1de134d1be 100644 --- a/testing/functional_testing/math_utils/FatesTestMathUtils.F90 +++ b/testing/functional_testing/math_utils/FatesTestMathUtils.F90 @@ -1,7 +1,7 @@ program FatesTestQuadSolvers use FatesConstantsMod, only : r8 => fates_r8 - use FatesUtilsMod, only : QuadraticRootsNSWC, QuadraticRootsSridharachary + use FatesUtilsMod, only : QuadraticRootsNSWC use FatesUtilsMod, only : GetNeighborDistance implicit none @@ -15,6 +15,7 @@ program FatesTestQuadSolvers real(r8) :: a(n), b(n), c(n) ! coefficients for quadratic solvers real(r8) :: root1(n) ! real part of first root of quadratic solver real(r8) :: root2(n) ! real part of second root of quadratic solver + logical :: err ! error interface @@ -42,7 +43,7 @@ end subroutine WriteQuadData c = (/1.0_r8, 12.0_r8, 3.0_r8, 1.1_r8/) do i = 1, n - call QuadraticRootsNSWC(a(i), b(i), c(i), root1(i), root2(i)) + call QuadraticRootsNSWC(a(i), b(i), c(i), root1(i), root2(i), err) end do call WriteQuadData(out_file, n, a, b, c, root1, root2) diff --git a/testing/functional_testing/math_utils/math_utils_test.py b/testing/functional_testing/math_utils/math_utils_test.py index 579838df18..df6d7fb173 100644 --- a/testing/functional_testing/math_utils/math_utils_test.py +++ b/testing/functional_testing/math_utils/math_utils_test.py @@ -5,7 +5,7 @@ import xarray as xr import numpy as np import matplotlib.pyplot as plt -from utils import get_color_palette +from utils_plotting import get_color_palette from functional_class import FunctionalTest diff --git a/testing/functional_testing/patch/patch_test.py b/testing/functional_testing/patch/patch_test.py index 7d4fec3e8d..0610fd521e 100644 --- a/testing/functional_testing/patch/patch_test.py +++ b/testing/functional_testing/patch/patch_test.py @@ -6,7 +6,8 @@ import pandas as pd import numpy as np import matplotlib.pyplot as plt -from utils import round_up, get_color_palette, blank_plot +from utils import round_up +from utils_plotting import blank_plot, get_color_palette from functional_class import FunctionalTest diff --git a/testing/functional_tests.cfg b/testing/functional_tests.cfg index 98d8448e42..fec8e075d6 100644 --- a/testing/functional_tests.cfg +++ b/testing/functional_tests.cfg @@ -17,7 +17,8 @@ test_dir = fates_fuel_ftest test_exe = FATES_fuel_exe out_file = fuel_out.nc use_param_file = True -other_args = ['../testing/test_data/BONA_datm.nc'] +datm_file = ../testing/test_data/BONA_datm.nc +other_args = [] [ros] test_dir = fates_ros_ftest @@ -32,3 +33,10 @@ test_exe = FATES_patch_exe out_file = None use_param_file = True other_args = [] + +[fire_mortality] +test_dir = fates_firemort_ftest +test_exe = FATES_firemort_exe +out_file = fire_mortality_out.nc +use_param_file = True +other_args = [] diff --git a/testing/load_functional_tests.py b/testing/load_functional_tests.py index 7b2051f15d..3b72dcb2b7 100644 --- a/testing/load_functional_tests.py +++ b/testing/load_functional_tests.py @@ -1,8 +1,10 @@ # add testing subclasses here from functional_class import FunctionalTest +from functional_class_with_drivers import FunctionalTestWithDrivers from functional_testing.allometry.allometry_test import AllometryTest from functional_testing.math_utils.math_utils_test import QuadraticTest from functional_testing.fire.fuel.fuel_test import FuelTest from functional_testing.fire.ros.ros_test import ROSTest from functional_testing.patch.patch_test import PatchTest +from functional_testing.fire.mortality.fire_mortality_test import FireMortTest diff --git a/testing/run_functional_tests.py b/testing/run_functional_tests.py index 105c22b06a..a2fa655c49 100755 --- a/testing/run_functional_tests.py +++ b/testing/run_functional_tests.py @@ -28,9 +28,11 @@ """ import os import argparse +import subprocess import matplotlib.pyplot as plt from build_fortran_tests import build_tests, build_exists +from functional_class_with_drivers import FunctionalTestWithDrivers from path_utils import add_cime_lib_to_path from utils import copy_file, create_nc_from_cdl, config_to_dict, parse_test_list @@ -39,12 +41,20 @@ add_cime_lib_to_path() -from CIME.utils import run_cmd_no_fail +from CIME.utils import run_cmd # constants for this script -_DEFAULT_CONFIG_FILE = "functional_tests.cfg" -_DEFAULT_CDL_PATH = os.path.abspath("../parameter_files/fates_params_default.cdl") -_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../") +_FILE_DIR = os.path.dirname(__file__) +_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "functional_tests.cfg") +_DEFAULT_CDL_PATH = os.path.abspath( + os.path.join( + _FILE_DIR, + os.pardir, + "parameter_files", + "fates_params_default.cdl", + ) +) +_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir) _TEST_SUB_DIR = "testing" @@ -74,6 +84,13 @@ def commandline_args(): "parameter_files directory.\n", ) + parser.add_argument( + "--config-file", + type=str, + default=_DEFAULT_CONFIG_FILE, + help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'", + ) + parser.add_argument( "-b", "--build-dir", @@ -179,6 +196,12 @@ def check_arg_validity(args): ) check_build_dir(args.build_dir, args.test_dict) + # Check that config file exists and is a file + if not os.path.exists(args.config_file): + raise FileNotFoundError(args.config_file) + if not os.path.isfile(args.config_file): + raise RuntimeError(f"config 'file' is a directory: '{args.config_file}'") + def check_param_file(param_file): """Checks to see if param_file exists and is of the correct form (.nc or .cdl) @@ -196,7 +219,7 @@ def check_param_file(param_file): None, "Must supply parameter file with .cdl or .nc ending." ) if not os.path.isfile(param_file): - raise argparse.ArgumentError(None, f"Cannot find file {param_file}.") + raise FileNotFoundError(param_file) def check_build_dir(build_dir, test_dict): @@ -295,8 +318,11 @@ def run_functional_tests( if run_executables: print("Running executables") for _, test in test_dict.items(): - # prepend parameter file (if required) to argument list args = test.other_args + # prepend datm file (if required) to argument list + if isinstance(test, FunctionalTestWithDrivers) and test.datm_file: + args.insert(0, test.datm_file) + # prepend parameter file (if required) to argument list if test.use_param_file: args.insert(0, param_file) # run @@ -386,22 +412,41 @@ def run_fortran_exectuables(build_dir, test_dir, test_exe, run_dir, args): run_command.extend(args) os.chdir(run_dir) - out = run_cmd_no_fail(" ".join(run_command), combine_output=True) + cmd = " ".join(run_command) + stat, out, _ = run_cmd(cmd, combine_output=True) + if stat: + print(out) + raise subprocess.CalledProcessError(stat, cmd, out) print(out) +def get_test_subclasses(*argv): + """ + Given a FunctionalTest* class, find all its test subclasses. Do not include child + FunctionalTest* classes. + """ + test_subclasses = [] + for ftest_class in argv: + test_subclasses += [x for x in ftest_class.__subclasses__() if hasattr(x, "name")] + return test_subclasses + + def main(): """Main script Reads in command-line arguments and then runs the tests. """ - full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) - subclasses = FunctionalTest.__subclasses__() - args = commandline_args() + + full_test_dict = config_to_dict(args.config_file) config_dict = parse_test_list(full_test_dict, args.test_list) test_dict = {} + + # Get all the possible test subclasses. + subclasses = get_test_subclasses(FunctionalTest, FunctionalTestWithDrivers) + + # Associate each test in the config file with the appropriate test subclass for name in config_dict.keys(): test_class = list(filter(lambda subclass: subclass.name == name, subclasses))[ 0 diff --git a/testing/run_unit_tests.py b/testing/run_unit_tests.py index f9bd344b39..a0f84532d0 100755 --- a/testing/run_unit_tests.py +++ b/testing/run_unit_tests.py @@ -29,8 +29,9 @@ from CIME.utils import run_cmd_no_fail # pylint: disable=wrong-import-position,import-error,wrong-import-order # constants for this script -_CMAKE_BASE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../") -_DEFAULT_CONFIG_FILE = "unit_tests.cfg" +_FILE_DIR = os.path.dirname(os.path.abspath(__file__)) +_CMAKE_BASE_DIR = os.path.join(_FILE_DIR, os.pardir) +_DEFAULT_CONFIG_FILE = os.path.join(_FILE_DIR, "unit_tests.cfg") _TEST_SUB_DIR = "testing" @@ -58,6 +59,13 @@ def commandline_args(): "Will be created if it does not exist.\n", ) + parser.add_argument( + "--config-file", + type=str, + default=_DEFAULT_CONFIG_FILE, + help=f"Configuration file where test list is defined. Default: '{_DEFAULT_CONFIG_FILE}'", + ) + parser.add_argument( "--make-j", type=int, @@ -129,9 +137,8 @@ def main(): Reads in command-line arguments and then runs the tests. """ - full_test_dict = config_to_dict(_DEFAULT_CONFIG_FILE) - args = commandline_args() + full_test_dict = config_to_dict(args.config_file) test_dict = parse_test_list(full_test_dict, args.test_list) run_unit_tests( diff --git a/testing/test_data/BONA_datm.nc b/testing/test_data/BONA_datm.nc new file mode 100644 index 0000000000..5781ff1e8b --- /dev/null +++ b/testing/test_data/BONA_datm.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d0a72950d8289b555c57e00cefe32ad0cc80ff8b0739e755e40956d86f93922 +size 22212 diff --git a/testing/testing_shr/FatesArgumentUtils.F90 b/testing/testing_shr/FatesArgumentUtils.F90 index ed247fa157..2bdbf825d3 100644 --- a/testing/testing_shr/FatesArgumentUtils.F90 +++ b/testing/testing_shr/FatesArgumentUtils.F90 @@ -24,7 +24,7 @@ function command_line_arg(arg_position) if (n_args < arg_position) then write(*, '(a, i2, a, i2)') "Incorrect number of arguments: ", n_args, ". Should be at least", arg_position, "." - stop + call abort() end if call get_command_argument(arg_position, length=arglen) diff --git a/testing/testing_shr/FatesFactoryMod.F90 b/testing/testing_shr/FatesFactoryMod.F90 index e0fe2293c6..5f2cdee5fb 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -2,11 +2,13 @@ module FatesFactoryMod use FatesConstantsMod, only : r8 => fates_r8 use FatesConstantsMod, only : leaves_on, leaves_off - use FatesConstantsMod, only : itrue + use FatesConstantsMod, only : ievergreen + use FatesConstantsMod, only : ihard_season_decid use FatesConstantsMod, only : ihard_stress_decid use FatesConstantsMod, only : isemi_stress_decid use FatesConstantsMod, only : primaryland use FatesConstantsMod, only : sec_per_day, days_per_year + use FatesConstantsMod, only : default_regeneration use FatesGlobals, only : fates_log use FatesGlobals, only : endrun => fates_endrun use FatesCohortMod, only : fates_cohort_type @@ -55,7 +57,7 @@ module FatesFactoryMod use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : nleafage use FatesSizeAgeTypeIndicesMod, only : get_age_class_index - use EDParamsMod, only : regeneration_model + use FatesInterfaceTypesMod, only : hlm_regeneration_model use SyntheticPatchTypes, only : synthetic_patch_type use shr_log_mod, only : errMsg => shr_log_errMsg @@ -88,6 +90,8 @@ subroutine InitializeGlobals(step_size) element_pos(carbon12_element) = 1 call InitPRTGlobalAllometricCarbon() + hlm_regeneration_model = default_regeneration + allocate(ema_24hr) call ema_24hr%define(sec_per_day, step_size, moving_ema_window) allocate(fixed_24hr) @@ -103,8 +107,10 @@ subroutine InitializeGlobals(step_size) dinc_vai(i) = ED_val_vai_top_bin_width*ED_val_vai_width_increase_factor**(i-1) end do - do i = 1, nlevleaf - dlower_vai(i) = sum(dinc_vai(1:i)) + ! lower edges of VAI bins + dlower_vai(1) = 0._r8 + do i = 2,nlevleaf + dlower_vai(i) = dlower_vai(i-1) + dinc_vai(i-1) end do end subroutine InitializeGlobals @@ -305,27 +311,34 @@ subroutine CohortFactory(cohort, pft, can_lai, dbh, number, crown_damage, status end if ! set leaf elongation factors - if (prt_params%season_decid(pft) == itrue .and. status_local == leaves_off) then - elongf_leaf = 0.0_r8 - elongf_fnrt = 1.0_r8 - prt_params%phen_fnrt_drop_fraction(pft) - elongf_stem = 1.0_r8 - prt_params%phen_stem_drop_fraction(pft) - - else if (any(prt_params%stress_decid(pft) == [ihard_stress_decid, isemi_stress_decid])) then + phen_select: select case (prt_params%phen_leaf_habit(pft)) + case (ihard_season_decid) + if (status_local == leaves_off) then + elongf_leaf = 0.0_r8 + elongf_fnrt = 1.0_r8 - prt_params%phen_fnrt_drop_fraction(pft) + elongf_stem = 1.0_r8 - prt_params%phen_stem_drop_fraction(pft) + else + elongf_leaf = 1.0_r8 + elongf_fnrt = 1.0_r8 + elongf_stem = 1.0_r8 + end if + + case (ihard_stress_decid, isemi_stress_decid) elongf_leaf = elong_fact_local elongf_fnrt = 1.0_r8 - (1.0_r8 - elongf_leaf)*prt_params%phen_fnrt_drop_fraction(pft) elongf_stem = 1.0_r8 - (1.0_r8 - elongf_leaf)*prt_params%phen_stem_drop_fraction(pft) - - if (elongf_leaf > 0.0_r8) then + + if (elongf_leaf > 0.0_r8) then status_local = leaves_on else status_local = leaves_off end if - - else + + case (ievergreen) elongf_leaf = 1.0_r8 - elongf_fnrt = 1.0_r8 - elongf_stem = 1.0_r8 - end if + elongf_fnrt = 1.0_r8 + elongf_stem = 1.0_r8 + end select phen_select ! calculate allometric properties @@ -435,7 +448,7 @@ subroutine PatchFactory(patch, age, area, num_swb, num_pft, num_levsoil, allocate(patch) call patch%Create(age, area, land_use_label_local, nocomp_pft_local, num_swb, & - num_pft, num_levsoil, tod_local, regeneration_model) + num_pft, num_levsoil, tod_local, hlm_regeneration_model) patch%patchno = 1 patch%younger => null() @@ -508,7 +521,7 @@ subroutine CreateTestPatchList(patch, heights, dbhs) if (present(dbhs)) then if (size(heights) /= size(dbhs)) then write(*, '(a)') "Size of heights array must match size of dbh array." - stop + call abort() end if end if @@ -533,4 +546,4 @@ subroutine CreateTestPatchList(patch, heights, dbhs) end subroutine CreateTestPatchList -end module FatesFactoryMod \ No newline at end of file +end module FatesFactoryMod diff --git a/testing/testing_shr/FatesUnitTestIOMod.F90 b/testing/testing_shr/FatesUnitTestIOMod.F90 index 20dd4f198e..ef261f1d0f 100644 --- a/testing/testing_shr/FatesUnitTestIOMod.F90 +++ b/testing/testing_shr/FatesUnitTestIOMod.F90 @@ -25,6 +25,7 @@ module FatesUnitTestIOMod interface WriteVar module procedure WriteVar1DReal module procedure WriteVar2DReal + module procedure WriteVar3DReal module procedure WriteVar1DInt module procedure WriteVar2DInt module procedure WriteVar1DChar @@ -106,7 +107,7 @@ subroutine Check(status) if (status /= nf90_noerr) then write(*,*) trim(nf90_strerror(status)) - stop + call abort() end if end subroutine Check @@ -134,11 +135,11 @@ subroutine OpenNCFile(nc_file, ncid, fmode) call Check(nf90_create(trim(nc_file), NF90_CLOBBER, ncid)) case DEFAULT write(*,*) 'Need to specify read, write, or readwrite' - stop + call abort() end select else write(*,*) 'Problem reading file' - stop + call abort() end if end subroutine OpenNCFile @@ -480,7 +481,7 @@ subroutine RegisterVar(ncid, var_name, dimID, type, att_names, atts, num_atts, v nc_type = NF90_CHAR else write(*, *) "Must pick correct type" - stop + call abort() end if call Check(nf90_def_var(ncid, var_name, nc_type, dimID, varID)) @@ -539,6 +540,24 @@ subroutine WriteVar2DReal(ncid, varID, data) call Check(nf90_put_var(ncid, varID, data(:,:))) end subroutine WriteVar2DReal + + + ! ===================================================================================== + + subroutine WriteVar3DReal(ncid, varID, data) + ! + ! DESCRIPTION: + ! Write 2D real data + ! + + ! ARGUMENTS: + integer, intent(in) :: ncid ! netcdf file id + integer, intent(in) :: varID ! variable ID + real(r8), intent(in) :: data(:,:,:) ! data to write + + call Check(nf90_put_var(ncid, varID, data(:,:,:))) + + end subroutine WriteVar3DReal ! ===================================================================================== diff --git a/testing/testing_shr/FatesUnitTestParamReaderMod.F90 b/testing/testing_shr/FatesUnitTestParamReaderMod.F90 index 2a4fb13cd8..d8b655136b 100644 --- a/testing/testing_shr/FatesUnitTestParamReaderMod.F90 +++ b/testing/testing_shr/FatesUnitTestParamReaderMod.F90 @@ -89,7 +89,7 @@ subroutine ReadParameters(this, fates_params) case default write(*, '(a,a)') 'dimension shape:', dimension_shape write(*, '(a)') 'unsupported number of dimensions reading parameters.' - stop + call abort() end select end do diff --git a/testing/testing_shr/SyntheticPatchTypes.F90 b/testing/testing_shr/SyntheticPatchTypes.F90 index 6094f0c6da..64747e5b6d 100644 --- a/testing/testing_shr/SyntheticPatchTypes.F90 +++ b/testing/testing_shr/SyntheticPatchTypes.F90 @@ -163,7 +163,7 @@ integer function PatchDataPosition(this, patch_id, patch_name) ! can't supply both if (present(patch_id) .and. present(patch_name)) then write(*, '(a)') "Can only supply either a patch_id or a patch_name - not both" - stop + call abort() end if do i = 1, this%num_patches @@ -179,11 +179,11 @@ integer function PatchDataPosition(this, patch_id, patch_name) end if else write(*, '(a)') "Must supply either a patch_id or a patch_name." - stop + call abort() end if end do write(*, '(a)') "Cannot find the synthetic patch type supplied" - stop + call abort() end function PatchDataPosition diff --git a/testing/unit_testing/fire_equations_test/test_FireEquations.pf b/testing/unit_testing/fire_equations_test/test_FireEquations.pf index c81d58b524..a597e22749 100644 --- a/testing/unit_testing/fire_equations_test/test_FireEquations.pf +++ b/testing/unit_testing/fire_equations_test/test_FireEquations.pf @@ -5,6 +5,7 @@ module test_FireEquations ! use FatesConstantsMod, only : r8 => fates_r8 use FatesConstantsMod, only : nearzero + use FatesUnitTestUtils, only : endrun_msg use SFEquationsMod use funit @@ -876,4 +877,274 @@ module test_FireEquations end subroutine FireSize_ZeroROS_ReturnsZero + @Test + subroutine ScorchHeight_FIZero_ReturnsZero(this) + ! test that if fire intensity is zero, scorch height is zero + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: SH ! sorch height [m] + real(r8), parameter :: alpha_SH = 0.5_r8 ! input alpha_SH parameter + real(r8), parameter :: FI = 0.0_r8 ! input fire intensity [kW/m] + + SH = ScorchHeight(alpha_SH, FI) + @assertEqual(SH, 0.0_r8) + + end subroutine ScorchHeight_FIZero_ReturnsZero + + @Test + subroutine ScorchHeight_AlphaZero_ReturnsZero(this) + ! test that if the alpha parameter is zero, scorch height is zero + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: SH ! sorch height [m] + real(r8), parameter :: alpha_SH = 0.0_r8 ! input alpha_SH parameter + real(r8), parameter :: FI = 1000.0_r8 ! input fire intensity [kW/m] + + SH = ScorchHeight(alpha_SH, FI) + @assertEqual(SH, 0.0_r8) + + end subroutine ScorchHeight_AlphaZero_ReturnsZero + + @Test + subroutine ScorchHeight_FINegative_ReturnsZero(this) + ! test that if fire intensity is negative, scorch height is zero + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: SH ! sorch height [m] + real(r8), parameter :: alpha_SH = 0.5_r8 ! input alpha_SH parameter + real(r8), parameter :: FI = -1000.0_r8 ! input fire intensity [kW/m] + + SH = ScorchHeight(alpha_SH, FI) + @assertEqual(SH, 0.0_r8) + + end subroutine ScorchHeight_FINegative_ReturnsZero + + @Test + subroutine CrownFractionBurnt_BasicInputs_CapsAtOne(this) + ! test that the function correctly caps crown fraction burnt at 1.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CF ! crown fraction burnt [0-1] + real(r8), parameter :: SH = 10.0_r8 ! input scorch height [m] + real(r8), parameter :: height = 4.0_r8 ! input tree height [m] + real(r8), parameter :: crown_depth = 2.0_r8 ! input crown depth [m] + + CF = CrownFractionBurnt(SH, height, crown_depth) + @assertEqual(CF, 1.0_r8) + + end subroutine CrownFractionBurnt_BasicInputs_CapsAtOne + + @Test + subroutine CrownFractionBurnt_CrownDepthZero_ReturnsZero(this) + ! test that the function sets crown fraction to zero if the crown depth is zero + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CF ! crown fraction burnt [0-1] + real(r8), parameter :: SH = 10.0_r8 ! input scorch height [m] + real(r8), parameter :: height = 4.0_r8 ! input tree height [m] + real(r8), parameter :: crown_depth = 0.0_r8 ! input crown depth [m] + + CF = CrownFractionBurnt(SH, height, crown_depth) + @assertEqual(CF, 0.0_r8) + + end subroutine CrownFractionBurnt_CrownDepthZero_ReturnsZero + + @Test + subroutine CrownFractionBurnt_ScorchHeightBelowCrownHeight_ReturnsZero(this) + ! test that the function sets crown fraction to zero if the scorch height is below crown height + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CF ! crown fraction burnt [0-1] + real(r8), parameter :: SH = 2.0_r8 ! input scorch height [m] + real(r8), parameter :: height = 4.0_r8 ! input tree height [m] + real(r8), parameter :: crown_depth = 2.0_r8 ! input crown depth [m] + + CF = CrownFractionBurnt(SH, height, crown_depth) + @assertEqual(CF, 0.0_r8) + + end subroutine CrownFractionBurnt_ScorchHeightBelowCrownHeight_ReturnsZero + + @Test + subroutine CrownFractionBurnt_ScorchHeightLow_ReturnsLessThanOne(this) + ! test that the function correctly calculates crownfraction burnt for SH below tree height but within the crown + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CF ! crown fraction burnt [0-1] + real(r8), parameter :: SH = 3.0_r8 ! input scorch height [m] + real(r8), parameter :: height = 4.0_r8 ! input tree height [m] + real(r8), parameter :: crown_depth = 2.0_r8 ! input crown depth [m] + + CF = CrownFractionBurnt(SH, height, crown_depth) + @assertGreaterThanOrEqual(CF, 0.0_r8) + @assertLessThanOrEqual(CF, 1.0_r8) + + end subroutine CrownFractionBurnt_ScorchHeightLow_ReturnsLessThanOne + + @Test + subroutine BarkThickness_NegativeBT_Errors(this) + ! test that if the bark thickness is negative, the function errors + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: BT ! bark thickness [cm] + real(r8), parameter :: bark_scalar = 0.01_r8 ! input bark scalar parameter + real(r8), parameter :: dbh = -10.0_r8 ! diameter at breast height [cm] + character(len=:), allocatable :: expected_msg ! expected error message for failure + + expected_msg = endrun_msg("bark thickness is negative") + + BT = BarkThickness(bark_scalar, dbh) + @assertExceptionRaised(expected_msg) + + end subroutine BarkThickness_NegativeBT_Errors + + @Test + subroutine CambialMortality_TauRGreaterThanTwo_ReturnsOne(this) + ! test that the function returns 1.0 if tau_r >= 2.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CM ! cambial mortality [0-1] + real(r8), parameter :: bark_scalar = 0.01_r8 ! input bark scalar parameter + real(r8), parameter :: dbh = 10.0_r8 ! input tree diameter at breast height [cm] + real(r8), parameter :: tau_l = 5.0_r8 ! input fire residence time [min] + + CM = CambialMortality(bark_scalar, dbh, tau_l) + @assertEqual(CM, 1.0_r8) + + end subroutine CambialMortality_TauRGreaterThanTwo_ReturnsOne + + @Test + subroutine CambialMortality_TaulZero_ReturnsZero(this) + ! test that the function returns 0.0 if tau_l is zero + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CM ! cambial mortality [0-1] + real(r8), parameter :: bark_scalar = 0.1_r8 ! input bark scalar parameter + real(r8), parameter :: dbh = 10.0_r8 ! input tree diameter at breast height [cm] + real(r8), parameter :: tau_l = 0.0_r8 ! input fire residence time [min] + + CM = CambialMortality(bark_scalar, dbh, tau_l) + @assertEqual(CM, 0.0_r8) + + end subroutine CambialMortality_TaulZero_ReturnsZero + + @Test + subroutine CrownFireMortality_ZeroFractionBurned_ReturnsZero(this) + ! test that the function returns 0.0 if fraction_crown_burned is 0.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CM ! crown fire mortality [0-1] + real(r8), parameter :: crown_kill = 0.1_r8 ! input crown kill parameter + real(r8), parameter :: fraction_crown_burned = 0.0 ! input fraction burned [0-1] + + CM = CrownFireMortality(crown_kill, fraction_crown_burned) + @assertEqual(CM, 0.0_r8) + + end subroutine CrownFireMortality_ZeroFractionBurned_ReturnsZero + + @Test + subroutine CrownFireMortality_FractionBurnedOne_ReturnsCrownKill(this) + ! test that the function returns crown_kill if fraction_crown_burned is 1.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CM ! crown fire mortality [0-1] + real(r8), parameter :: crown_kill = 0.1_r8 ! input crown kill parameter + real(r8), parameter :: fraction_crown_burned = 1.0 ! input fraction burned [0-1] + + CM = CrownFireMortality(crown_kill, fraction_crown_burned) + @assertEqual(CM, crown_kill) + + end subroutine CrownFireMortality_FractionBurnedOne_ReturnsCrownKill + + @Test + subroutine CrownFireMortality_HighCrownKill_ReturnsOne(this) + ! test that the function returns 1.0 even if crown_kill is very high + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: CM ! crown fire mortality [0-1] + real(r8), parameter :: crown_kill = 10.0_r8 ! input crown kill parameter + real(r8), parameter :: fraction_crown_burned = 0.5_r8 ! input fraction burned [0-1] + + CM = CrownFireMortality(crown_kill, fraction_crown_burned) + @assertEqual(CM, 1.0_r8) + + end subroutine CrownFireMortality_HighCrownKill_ReturnsOne + + @Test + subroutine TotalFireMortality_ReasonableValues_ReturnsReasonable(this) + ! test that the function returns correct values given reasonable inputs + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 0.4_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 0.3_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, 0.58_r8) + + end subroutine TotalFireMortality_ReasonableValues_ReturnsReasonable + + @Test + subroutine TotalFireMortality_ZeroInputs_ReturnsZero(this) + ! test that the function returns zero if both input mortality rates are 0.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 0.0_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 0.0_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, 0.0_r8) + + end subroutine TotalFireMortality_ZeroInputs_ReturnsZero + + @Test + subroutine TotalFireMortality_OneInputs_ReturnsOne(this) + ! test that the function returns one if both input mortality rates are 1.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 1.0_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 1.0_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, 1.0_r8) + + end subroutine TotalFireMortality_OneInputs_ReturnsOne + + @Test + subroutine TotalFireMortality_ZeroCrownFire_ReturnsCambial(this) + ! test that the function returns cambial_damage_mort if crownfire_mort = 0.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 0.0_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 0.3_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, cambial_damage_mort) + + end subroutine TotalFireMortality_ZeroCrownFire_ReturnsCambial + + @Test + subroutine TotalFireMortality_ZeroCambial_ReturnsCrownFire(this) + ! test that the function returns crownfire_mort if cambial_damage_mort = 0.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 0.4_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 0.0_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, crownfire_mort) + + end subroutine TotalFireMortality_ZeroCambial_ReturnsCrownFire + + @Test + subroutine TotalFireMortality_OverOneMort_ReturnsOne(this) + ! test that the function correctly caps mortality rate at 1.0 + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = 1.4_r8 ! crown fire mortality [0-1] + real(r8), parameter :: cambial_damage_mort = 1.4_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, 1.0_r8) + + end subroutine TotalFireMortality_OverOneMort_ReturnsOne + + @Test + subroutine TotalFireMortality_NegativeMortality_ReturnsZero(this) + ! test that the function correctly gives zero if somehow mortality rates are negative + class(TestFireEquations), intent(inout) :: this ! test object + real(r8) :: TM ! total fire mortality [0-1] + real(r8), parameter :: crownfire_mort = -1.4_r8 ! input crown kill parameter + real(r8), parameter :: cambial_damage_mort = -1.4_r8 ! input fraction burned [0-1] + + TM = TotalFireMortality(crownfire_mort, cambial_damage_mort) + @assertEqual(TM, 0.0_r8) + + end subroutine TotalFireMortality_NegativeMortality_ReturnsZero + end module test_FireEquations diff --git a/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf b/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf index 9ee33910f3..7d8c0b1110 100644 --- a/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf +++ b/testing/unit_testing/sort_cohorts_test/test_SortCohorts.pf @@ -24,7 +24,7 @@ module test_SortCohorts class(TestSortCohorts), intent(inout) :: this ! test object type(fates_patch_type) :: patch ! patch object - ! sort cohorts - should pass + ! sort cohorts - should pass - the argument call patch%SortCohorts() end subroutine EmptyList_SortCohorts_Passes diff --git a/testing/utils.py b/testing/utils.py index 06fd74db0b..55978cc7d0 100644 --- a/testing/utils.py +++ b/testing/utils.py @@ -1,11 +1,11 @@ -"""Utility functions for plotting, file checking, math equations, etc. +"""Utility functions for file checking, math equations, etc. +Do not include any third-party modules here. """ import math import os import configparser import argparse -import matplotlib.pyplot as plt from path_utils import add_cime_lib_to_path add_cime_lib_to_path() @@ -76,51 +76,25 @@ def copy_file(file_path: str, directory) -> str: return file_basename -def get_color_palette(number: int) -> list: - """_summary_ +def get_abspath_from_config_file(relative_path, config_file): + """ + Gets the absolute path of a file relative to the config file where it was defined. Args: - number (int): number of colors to get - must be <= 20 - - Raises: - ValueError: number must be less than hard-coded list + relative_path: The path to the target file, relative to the base file. + config_file: The path to the config file. Returns: - list[tuple]: list of colors to use in plotting + The absolute path of the target file. """ - # hard-coded list of colors, can add more here if necessary - all_colors = [ - (31, 119, 180), - (174, 199, 232), - (255, 127, 14), - (255, 187, 120), - (44, 160, 44), - (152, 223, 138), - (214, 39, 40), - (255, 152, 150), - (148, 103, 189), - (197, 176, 213), - (140, 86, 75), - (196, 156, 148), - (227, 119, 194), - (247, 182, 210), - (127, 127, 127), - (199, 199, 199), - (188, 189, 34), - (219, 219, 141), - (23, 190, 207), - (158, 218, 229), - ] - - if number > len(all_colors): - raise ValueError(f"get_color_palette: number must be <= {len(all_colors)}") - - colors = [ - (red / 255.0, green / 255.0, blue / 255.0) for red, green, blue in all_colors - ] - - return colors[:number] + # Do nothing if it's already a absolute path + if os.path.isabs(relative_path): + return relative_path + + base_dir = os.path.dirname(os.path.abspath(config_file)) + absolute_path = os.path.abspath(os.path.join(base_dir, relative_path)) + return absolute_path def config_to_dict(config_file: str) -> dict: @@ -132,6 +106,10 @@ def config_to_dict(config_file: str) -> dict: Returns: dictionary: dictionary of config file """ + + # Define list of config file options that we expect to be paths + options_that_are_paths = ["datm_file"] + config = configparser.ConfigParser() config.read(config_file) @@ -139,7 +117,14 @@ def config_to_dict(config_file: str) -> dict: for section in config.sections(): dictionary[section] = {} for option in config.options(section): - dictionary[section][option] = config.get(section, option) + value = config.get(section, option) + + # If the option is one that we expect to be a path, ensure it's an absolute path. + if option in options_that_are_paths: + value = get_abspath_from_config_file(value, config_file) + + # Save value to dictionary + dictionary[section][option] = value return dictionary @@ -208,52 +193,3 @@ def str_to_list(val: str) -> list: return [] res = val.strip("][").split(",") return [n.strip() for n in res] - - -def blank_plot( - x_max: float, - x_min: float, - y_max: float, - y_min: float, - draw_horizontal_lines: bool = False, -): - """Generate a blank plot with set attributes - - Args: - x_max (float): maximum x value - x_min (float): minimum x value - y_max (float): maximum y value - y_min (float): minimum y value - draw_horizontal_lines (bool, optional): whether or not to draw horizontal - lines across plot. Defaults to False. - """ - - plt.figure(figsize=(7, 5)) - axis = plt.subplot(111) - axis.spines["top"].set_visible(False) - axis.spines["bottom"].set_visible(False) - axis.spines["right"].set_visible(False) - axis.spines["left"].set_visible(False) - - axis.get_xaxis().tick_bottom() - axis.get_yaxis().tick_left() - - plt.xlim(0.0, x_max) - plt.ylim(0.0, y_max) - - plt.yticks(fontsize=10) - plt.xticks(fontsize=10) - - if draw_horizontal_lines: - inc = (int(y_max) - y_min) / 20 - for i in range(0, 20): - plt.plot( - range(math.floor(x_min), math.ceil(x_max)), - [0.0 + i * inc] * len(range(math.floor(x_min), math.ceil(x_max))), - "--", - lw=0.5, - color="black", - alpha=0.3, - ) - - plt.tick_params(bottom=False, top=False, left=False, right=False) diff --git a/testing/utils_plotting.py b/testing/utils_plotting.py new file mode 100644 index 0000000000..df38b80617 --- /dev/null +++ b/testing/utils_plotting.py @@ -0,0 +1,101 @@ +"""Utility functions for plotting +""" + +import math +import matplotlib.pyplot as plt + + +def blank_plot( + x_max: float, + x_min: float, + y_max: float, + y_min: float, + draw_horizontal_lines: bool = False, +): + """Generate a blank plot with set attributes + + Args: + x_max (float): maximum x value + x_min (float): minimum x value + y_max (float): maximum y value + y_min (float): minimum y value + draw_horizontal_lines (bool, optional): whether or not to draw horizontal + lines across plot. Defaults to False. + """ + + plt.figure(figsize=(7, 5)) + axis = plt.subplot(111) + axis.spines["top"].set_visible(False) + axis.spines["bottom"].set_visible(False) + axis.spines["right"].set_visible(False) + axis.spines["left"].set_visible(False) + + axis.get_xaxis().tick_bottom() + axis.get_yaxis().tick_left() + + plt.xlim(0.0, x_max) + plt.ylim(0.0, y_max) + + plt.yticks(fontsize=10) + plt.xticks(fontsize=10) + + if draw_horizontal_lines: + inc = (int(y_max) - y_min) / 20 + for i in range(0, 20): + plt.plot( + range(math.floor(x_min), math.ceil(x_max)), + [0.0 + i * inc] * len(range(math.floor(x_min), math.ceil(x_max))), + "--", + lw=0.5, + color="black", + alpha=0.3, + ) + + plt.tick_params(bottom=False, top=False, left=False, right=False) + + +def get_color_palette(number: int) -> list: + """_summary_ + + Args: + number (int): number of colors to get - must be <= 20 + + Raises: + ValueError: number must be less than hard-coded list + + Returns: + list[tuple]: list of colors to use in plotting + """ + + # hard-coded list of colors, can add more here if necessary + all_colors = [ + (31, 119, 180), + (174, 199, 232), + (255, 127, 14), + (255, 187, 120), + (44, 160, 44), + (152, 223, 138), + (214, 39, 40), + (255, 152, 150), + (148, 103, 189), + (197, 176, 213), + (140, 86, 75), + (196, 156, 148), + (227, 119, 194), + (247, 182, 210), + (127, 127, 127), + (199, 199, 199), + (188, 189, 34), + (219, 219, 141), + (23, 190, 207), + (158, 218, 229), + ] + + if number > len(all_colors): + raise ValueError(f"get_color_palette: number must be <= {len(all_colors)}") + + colors = [ + (red / 255.0, green / 255.0, blue / 255.0) for red, green, blue in all_colors + ] + + return colors[:number] diff --git a/tools/UpdateParamAPI.py b/tools/UpdateParamAPI.py index b158e2961e..ba921351c8 100755 --- a/tools/UpdateParamAPI.py +++ b/tools/UpdateParamAPI.py @@ -350,6 +350,7 @@ def main(): if(dimnames[0]=='scalar' or dimnames[0]=='none' or dimnames[0]==''): dimnames = () + dcode = "d" elif(isinstance(values[0],float)): dcode = "d" else: From 8fa0c364d86fc4a70f26f60b987cb67abc85bfc9 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Sun, 12 Oct 2025 12:20:17 -0600 Subject: [PATCH 172/194] small fix to ignore val global --- biogeochem/FatesPatchMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index 5c904d0a96..e999e5aba9 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -606,7 +606,7 @@ subroutine ZeroValues(this) this%fabd(:) = 0.0_r8 this%sabs_dir(:) = 0.0_r8 this%sabs_dif(:) = 0.0_r8 - this%rad_error(:) = hlm_hio_ignore_value + this%rad_error(:) = hlm_hio_ignore_val ! ROOTS this%btran_ft(:) = 0.0_r8 From 7ba3727d38854cd1c765d005bb0312c6da119fb4 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Sun, 12 Oct 2025 17:26:38 -0600 Subject: [PATCH 173/194] small fixes for rad_error and parprofile refactors --- biogeochem/FatesPatchMod.F90 | 15 ------------ main/FatesHistoryInterfaceMod.F90 | 23 ++++++++++-------- main/FatesInterfaceMod.F90 | 2 +- main/FatesRestartInterfaceMod.F90 | 4 ++-- radiation/FatesRadiationDriveMod.F90 | 35 +++++++++++++++++----------- 5 files changed, 37 insertions(+), 42 deletions(-) diff --git a/biogeochem/FatesPatchMod.F90 b/biogeochem/FatesPatchMod.F90 index e999e5aba9..2f358cda63 100644 --- a/biogeochem/FatesPatchMod.F90 +++ b/biogeochem/FatesPatchMod.F90 @@ -172,11 +172,6 @@ module FatesPatchMod real(r8),allocatable :: ed_laisun_z(:,:,:) !nclmax,maxpft,nlevleaf) real(r8),allocatable :: ed_laisha_z(:,:,:) !nclmax,maxpft,nlevleaf) - - ! radiation profiles for comparison against observations - real(r8),allocatable :: parprof_pft_dir_z(:,:,:) !nclmax,maxpft,nlevleaf) ! direct-beam PAR profile through canopy, by canopy, PFT, leaf level [W/m2] - real(r8),allocatable :: parprof_pft_dif_z(:,:,:) !nclmax,maxpft,nlevleaf) ! diffuse PAR profile through canopy, by canopy, PFT, leaf level [W/m2] - real(r8), allocatable :: tr_soil_dir(:) ! fraction of incoming direct radiation transmitted to the soil as direct, by numSWB [0-1] real(r8), allocatable :: tr_soil_dif(:) ! fraction of incoming diffuse radiation that is transmitted to the soil as diffuse [0-1] real(r8), allocatable :: tr_soil_dir_dif(:) ! fraction of incoming direct radiation that is transmitted to the soil as diffuse [0-1] @@ -372,8 +367,6 @@ subroutine ReAllocateDynamics(this) deallocate(this%ed_parsha_z) deallocate(this%ed_laisun_z) deallocate(this%ed_laisha_z) - deallocate(this%parprof_pft_dir_z) - deallocate(this%parprof_pft_dif_z) deallocate(this%canopy_area_profile) else ! The number of canopy layers has not changed @@ -407,8 +400,6 @@ subroutine ReAllocateDynamics(this) allocate(this%ed_parsha_z(ncan,numpft,nveg)) allocate(this%ed_laisun_z(ncan,numpft,nveg)) allocate(this%ed_laisha_z(ncan,numpft,nveg)) - allocate(this%parprof_pft_dir_z(ncan,numpft,nveg)) - allocate(this%parprof_pft_dif_z(ncan,numpft,nveg)) end if return @@ -437,8 +428,6 @@ subroutine NanDynamics(this) this%ed_parsun_z(:,:,:) = nan this%ed_parsha_z(:,:,:) = nan this%f_sun(:,:,:) = nan - this%parprof_pft_dir_z(:,:,:) = nan - this%parprof_pft_dif_z(:,:,:) = nan end subroutine NanDynamics @@ -573,8 +562,6 @@ subroutine ZeroDynamics(this) this%ed_laisha_z(:,:,:) = 0._r8 this%ed_parsun_z(:,:,:) = 0._r8 this%ed_parsha_z(:,:,:) = 0._r8 - this%parprof_pft_dir_z(:,:,:) = 0._r8 - this%parprof_pft_dif_z(:,:,:) = 0._r8 end subroutine ZeroDynamics @@ -944,8 +931,6 @@ subroutine FreeMemory(this, regeneration_model, numpft) deallocate(this%ed_parsha_z) deallocate(this%ed_laisun_z) deallocate(this%ed_laisha_z) - deallocate(this%parprof_pft_dir_z) - deallocate(this%parprof_pft_dif_z) deallocate(this%canopy_area_profile) end if diff --git a/main/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index cef49754fd..ee8cc1ed3c 100644 --- a/main/FatesHistoryInterfaceMod.F90 +++ b/main/FatesHistoryInterfaceMod.F90 @@ -68,7 +68,7 @@ module FatesHistoryInterfaceMod use FatesInterfaceTypesMod , only : nlevcoage use FatesInterfaceTypesMod , only : hlm_use_nocomp use FatesInterfaceTypesMod , only : hlm_use_fixed_biogeog - use FatesRadiationMemMod , only : ivis,inir + use FatesRadiationMemMod , only : ivis,inir,ipar use FatesInterfaceTypesMod , only : hlm_hist_level_hifrq,hlm_hist_level_dynam use FatesIOVariableKindMod, only : site_r8, site_soil_r8, site_size_pft_r8 use FatesIOVariableKindMod, only : site_size_r8, site_pft_r8, site_age_r8 @@ -5123,7 +5123,7 @@ subroutine update_history_hifrq(this,nc,nsites,sites,bc_in,bc_out,dt_tstep) if(hlm_hist_level_hifrq>0) then call update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,dt_tstep) if(hlm_hist_level_hifrq>1) then - call update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) + call update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,dt_tstep) call update_history_hifrq_subsite_ageclass(this,nsites,sites,dt_tstep) end if end if @@ -5358,7 +5358,7 @@ end subroutine update_history_hifrq_sitelevel ! =============================================================================================== - subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) + subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,dt_tstep) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -5373,7 +5373,8 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) class(fates_history_interface_type) :: this integer , intent(in) :: nc ! clump index integer , intent(in) :: nsites - type(ed_site_type) , intent(inout), target :: sites(nsites) + type(ed_site_type) , intent(inout) :: sites(nsites) + type(bc_in_type) , intent(in) :: bc_in(nsites) real(r8) , intent(in) :: dt_tstep ! Locals @@ -5461,8 +5462,10 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) io_si = sites(s)%h_gid cpatch => sites(s)%oldest_patch - do while(associated(cpatch)) - + patch_loop1: do while(associated(cpatch)) + + nocomp_bare: if(cpatch%nocomp_pft_label.ne.nocomp_bareground)then + ccohort => cpatch%shortest do while(associated(ccohort)) @@ -5590,10 +5593,10 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) hio_laisha_clllpf(io_si,clllpf_indx) = hio_laisha_clllpf(io_si,clllpf_indx) + & cpatch%elai_profile(ican,ipft,ileaf)*(1._r8-cpatch%f_sun(ican,ipft,ileaf))*clllpf_area - parprof_pft_dir_z = bc_in(s)%solad_parb(ifp,ipar) * & + parprof_pft_dir_z = bc_in(s)%solad_parb(cpatch%patchno,ipar) * & cpatch%nrmlzd_parprof_pft_dir_z(ican,ipft,ileaf) - parprof_pft_dif_z = bc_in(s)%solai_parb(ifp,ipar) * & + parprof_pft_dif_z = bc_in(s)%solai_parb(cpatch%patchno,ipar) * & cpatch%nrmlzd_parprof_pft_dif_z(ican,ipft,ileaf) hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dir_si_cnlfpft(io_si,clllpf_indx) + & @@ -5646,9 +5649,9 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,dt_tstep) end do do_canlev1 end do do_pft1 end if if_zenith1 - + end if nocomp_bare cpatch => cpatch%younger - end do !patch loop + end do patch_loop1 !patch loop ! Normalize the radiation multiplexed diagnostics ! Set values that dont have canopy elements to ignore diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 3560aa84bb..6982dd700b 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -2409,7 +2409,7 @@ subroutine SeedlingParPatch(cpatch, & integer :: iv ! lower-most leaf layer index for the cl & pft combo ! Start with the assumption that there is a single canopy layer - seedling_par_high = atm_par_dir+arm_par_dif + seedling_par_high = atm_par_dir+atm_par_dif par_high_frac = 1._r8-cpatch%total_canopy_area par_low_frac = cpatch%total_canopy_area diff --git a/main/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index 6a30cd3f0d..d1d689fa62 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -4145,8 +4145,8 @@ subroutine update_3dpatch_radiation(this, nsites, sites, bc_out) currentPatch%fabi (:) = 0._r8 ! zero diagnostic radiation profiles - currentPatch%nrmlzd_parprof_pft_dir_z(:,:,:,:) = 0._r8 - currentPatch%nrmlzd_parprof_pft_dif_z(:,:,:,:) = 0._r8 + currentPatch%nrmlzd_parprof_pft_dir_z(:,:,:) = 0._r8 + currentPatch%nrmlzd_parprof_pft_dif_z(:,:,:) = 0._r8 currentPatch%rad_error(:) = hlm_hio_ignore_val if_notbareground: if(currentPatch%nocomp_pft_label.ne.nocomp_bareground) then diff --git a/radiation/FatesRadiationDriveMod.F90 b/radiation/FatesRadiationDriveMod.F90 index ae5e464d3b..a401388f7e 100644 --- a/radiation/FatesRadiationDriveMod.F90 +++ b/radiation/FatesRadiationDriveMod.F90 @@ -83,8 +83,13 @@ subroutine FatesNormalizedCanopyRadiation(sites, bc_in, bc_out ) integer :: nsites ! number of sites integer :: ifp ! patch loop counter integer :: ib ! radiation broad band counter - integer :: cl, icol, ft ! indices for canopy layer, + integer :: cl, iv, icol, ft ! indices for canopy layer,leaf layer, ! rad column and functional type + integer :: nv ! number of veg layers + real(r8) :: area_frac ! area fraction for layer of interest + real(r8) :: vai_top ! integrated (top-down) vegetation area + ! index at lop of layer + real(r8) :: vai ! total VAI of the scattering element type(fates_patch_type), pointer :: currentPatch ! patch pointer !----------------------------------------------------------------------- @@ -128,8 +133,8 @@ subroutine FatesNormalizedCanopyRadiation(sites, bc_in, bc_out ) currentPatch%fabi_sha_z (:,:,:) = 0._r8 currentPatch%fabd (:) = 0._r8 currentPatch%fabi (:) = 0._r8 - currentPatch%nrmlzd_parprof_pft_dir_z(:,:,:,:) = 0._r8 - currentPatch%nrmlzd_parprof_pft_dif_z(:,:,:,:) = 0._r8 + currentPatch%nrmlzd_parprof_pft_dir_z(:,:,:) = 0._r8 + currentPatch%nrmlzd_parprof_pft_dif_z(:,:,:) = 0._r8 currentPatch%gnd_alb_dif(1:num_swb) = bc_in(s)%albgr_dif_rb(1:num_swb) currentPatch%gnd_alb_dir(1:num_swb) = bc_in(s)%albgr_dir_rb(1:num_swb) currentPatch%fcansno = bc_in(s)%fcansno_pa(ifp) @@ -198,17 +203,19 @@ subroutine FatesNormalizedCanopyRadiation(sites, bc_in, bc_out ) do_cl: do cl = 1,twostr%n_lyr do_icol: do icol = 1,twostr%n_col(cl) ft = twostr%scelg(cl,icol)%pft - nv = minloc(dlower_vai, DIM=1, MASK=(dlower_vai>vai)) - area_frac = twostr%scelg(cl,icol)%area - ! WAIT FOR THE BIN INDEXING PR TO GO IN ... - do iv = 1, nv - vai_top = dlower_vai(iv) - cpatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) = cpatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) + & - area_frac*twostr%GetRb(cl,icol,ivis,vai_top) - cpatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) = cpatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) + & - area_frac*twostr%GetRdDn(cl,icol,ivis,vai_top) + & - area_frac*twostr%GetRdUp(cl,icol,ivis,vai_top) - end do + if_notair: if (ft>0) then + area_frac = twostr%scelg(cl,icol)%area + vai = twostr%scelg(cl,icol)%sai+twostr%scelg(cl,icol)%lai + nv = GetNVegLayers(vai) + do iv = 1, nv + vai_top = dlower_vai(iv) + currentPatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) = currentPatch%nrmlzd_parprof_pft_dir_z(cl,ft,iv) + & + area_frac*twostr%GetRb(cl,icol,ivis,vai_top) + currentPatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) = currentPatch%nrmlzd_parprof_pft_dif_z(cl,ft,iv) + & + area_frac*twostr%GetRdDn(cl,icol,ivis,vai_top) + & + area_frac*twostr%GetRdUp(cl,icol,ivis,vai_top) + end do + end if if_notair end do do_icol end do do_cl From 9c87850cbdc3da5ae120962b17dfaabc1408e5e5 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 13 Oct 2025 19:10:51 -0700 Subject: [PATCH 174/194] update xarray submodule name --- .gitmodules | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index cb98a15ece..02a4e46602 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,6 +11,8 @@ fxrequired = AlwaysRequired # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/NGEET/tools-fates-landusedata -[submodule "tools/xarray"] - path = tools/xarray - url = git@github.com:NGEET/tools-fates-xarray.git +[submodule "tools/xarray-functions"] + path = tools/xarray-functions + url = https://github.com/NGEET/tools-fates-xarray.git + fxtag = v0.0.0 + fxrequired = AlwaysRequired From e8227b21302c33bc93f6cbd1f8981878a8a2e7bd Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 13 Oct 2025 19:21:06 -0700 Subject: [PATCH 175/194] update xarray name and use latest hash --- tools/xarray | 1 - tools/xarray-functions | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 160000 tools/xarray create mode 160000 tools/xarray-functions diff --git a/tools/xarray b/tools/xarray deleted file mode 160000 index 61b88ca7c2..0000000000 --- a/tools/xarray +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 61b88ca7c2beb5751645b8f779a5541e86e50896 diff --git a/tools/xarray-functions b/tools/xarray-functions new file mode 160000 index 0000000000..b62d9333b5 --- /dev/null +++ b/tools/xarray-functions @@ -0,0 +1 @@ +Subproject commit b62d9333b5423e4759bd78b60e913dc1bbe214b7 From a338d6a127f423de157d5cd93d6efd380a8343f7 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Mon, 13 Oct 2025 19:22:27 -0700 Subject: [PATCH 176/194] update gitmodules with donotuseurl --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 02a4e46602..7fa78ed7ad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,4 @@ url = https://github.com/NGEET/tools-fates-xarray.git fxtag = v0.0.0 fxrequired = AlwaysRequired + fxDONOTUSEurl = https://github.com/NGEET/tools-fates-xarray From edaf2a40312c6043050f2e163173ef0f0ba8490f Mon Sep 17 00:00:00 2001 From: Jessica Needham Date: Mon, 27 Oct 2025 12:27:17 +0100 Subject: [PATCH 177/194] update secondary young frac calculation --- main/EDTypesMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index 90be4df5ec..c3ee5775d0 100644 --- a/main/EDTypesMod.F90 +++ b/main/EDTypesMod.F90 @@ -848,7 +848,7 @@ function get_secondary_young_fraction(this) result(secondary_young_fraction) currentPatch => this%oldest_patch do while (associated(currentPatch)) if (currentPatch%land_use_label .eq. secondaryland) then - if ( currentPatch%age .ge. secondary_age_threshold ) then + if ( currentPatch%age_since_anthro_disturbance .ge. secondary_age_threshold ) then secondary_old_area = secondary_old_area + currentPatch%area else secondary_young_area = secondary_young_area + currentPatch%area From 8c5a24ce880ac117dffa7f5b87427a57250af8ed Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 27 Oct 2025 08:13:31 -0600 Subject: [PATCH 178/194] changed l2fr fusion to only happen during CNP runs (for b4b) --- biogeochem/EDCohortDynamicsMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/biogeochem/EDCohortDynamicsMod.F90 b/biogeochem/EDCohortDynamicsMod.F90 index fca123a68d..ee1e2d4a7c 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -823,9 +823,6 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) ! Leaf biophysical rates (use leaf mass weighting) ! ----------------------------------------------------------------- call currentCohort%UpdateCohortBioPhysRates() - - currentCohort%l2fr = (currentCohort%n*currentCohort%l2fr & - + nextc%n*nextc%l2fr)/newn currentCohort%canopy_trim = (currentCohort%n*currentCohort%canopy_trim & + nextc%n*nextc%canopy_trim)/newn @@ -1044,6 +1041,9 @@ subroutine fuse_cohorts(currentSite, currentPatch, bc_in) ! Nutrients if(hlm_parteh_mode .eq. prt_cnp_flex_allom_hyp) then + currentCohort%l2fr = (currentCohort%n*currentCohort%l2fr & + + nextc%n*nextc%l2fr)/newn + if(nextc%n > currentCohort%n) currentCohort%cnp_limiter = nextc%cnp_limiter currentCohort%cx_int = (currentCohort%n*currentCohort%cx_int + & From 283bc8e51a0172d7f4a070555e3b96426063e813 Mon Sep 17 00:00:00 2001 From: Gregory Lemieux <7565064+glemieux@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:51:23 -0800 Subject: [PATCH 179/194] Update main/ChecksBalancesMod.F90 Per discussion with @rgknox --- main/ChecksBalancesMod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/ChecksBalancesMod.F90 b/main/ChecksBalancesMod.F90 index 5fb0f7ccf8..f041389fd3 100644 --- a/main/ChecksBalancesMod.F90 +++ b/main/ChecksBalancesMod.F90 @@ -302,7 +302,7 @@ subroutine CheckIntegratedMassPools(site) select case(element_list(el)) case(carbon12_element) - net_uptake = (site_mass%gpp_acc + site_mass%aresp_acc + site_mass%net_root_uptake)*area_inv + net_uptake = (site_mass%gpp_acc - site_mass%aresp_acc + site_mass%net_root_uptake)*area_inv case(nitrogen_element) net_uptake = site_mass%net_root_uptake*area_inv case(phosphorus_element) From 0d5d3eacb10c5f5db6d80d49e20dc6256ae5af0c Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 11 Nov 2025 20:08:17 +0100 Subject: [PATCH 180/194] bound treelai if leafc_per_unitarea is tiny) --- biogeochem/FatesAllometryMod.F90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/biogeochem/FatesAllometryMod.F90 b/biogeochem/FatesAllometryMod.F90 index d7e09393d4..94b58a881a 100644 --- a/biogeochem/FatesAllometryMod.F90 +++ b/biogeochem/FatesAllometryMod.F90 @@ -748,7 +748,8 @@ real(r8) function tree_lai( leaf_c, pft, c_area, nplant, cl, canopy_lai, vcmax25 tree_lai = (log(exp(-1.0_r8 * kn * canopy_lai_above) - & kn * slat * leafc_per_unitarea) + & (kn * canopy_lai_above)) / (-1.0_r8 * kn) - + ! precision errors in the above when leafc_per_unit_area is tiny can make treelai negative + tree_lai = max(0.0_r8,tree_lai) ! If leafc_per_unitarea becomes too large, tree_lai becomes an imaginary number ! (because the tree_lai equation requires us to take the natural log of something >0) ! Thus, we include the following error message in case leafc_per_unitarea becomes too large. @@ -774,7 +775,8 @@ real(r8) function tree_lai( leaf_c, pft, c_area, nplant, cl, canopy_lai, vcmax25 kn * slat * leafc_slamax) + & (kn * canopy_lai_above)) / (-1.0_r8 * kn)) + & (leafc_per_unitarea - leafc_slamax) * sla_max - + ! precision errors in the above when leafc_per_unit_area is tiny can make treelai negative + tree_lai = max(0.0_r8,tree_lai) ! if leafc_slamax becomes too large, tree_lai_exp becomes an imaginary number ! (because the tree_lai equation requires us to take the natural log of something >0) ! Thus, we include the following error message in case leafc_slamax becomes too large. From a6b69436f18546d6116fdfd5dd8f8a1570772594 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 17 Nov 2025 17:17:59 -0800 Subject: [PATCH 181/194] fixes to promotion demotion when operating around very small numbers --- biogeochem/EDCanopyStructureMod.F90 | 81 +++++++++++++++-------------- biogeochem/FatesCohortMod.F90 | 4 -- main/FatesUtilsMod.F90 | 8 --- 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index e21e1c56e7..463a5712de 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -206,6 +206,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! Terminate cohorts before organizing canopy. That ! step will be interested in preserving area, so termination ! during that step will be counter productive + call terminate_cohorts(currentSite, currentPatch, -1,13,bc_in) call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) @@ -216,6 +217,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! canopy layer has a special bounds check currentCohort => currentPatch%tallest do while (associated(currentCohort)) + currentCohort%canopy_layer_yesterday = currentCohort%canopy_layer if( currentCohort%canopy_layer < 1 ) then write(fates_log(),*) 'lat:',currentSite%lat write(fates_log(),*) 'lon:',currentSite%lon @@ -248,6 +250,8 @@ subroutine canopy_structure( currentSite , bc_in ) call PromoteOrDemote(currentSite, currentPatch, i_lyr, demotion_phase, target_area) end do + ! Terminate only for type 1 (near zero number density) + call terminate_cohorts(currentSite, currentPatch,1,23,bc_in) call fuse_cohorts(currentSite, currentPatch, bc_in) ! --------------------------------------------------------------------------------------- @@ -266,6 +270,8 @@ subroutine canopy_structure( currentSite , bc_in ) call PromoteOrDemote(currentSite, currentPatch, i_lyr, promotion_phase, target_area) end do + ! Terminate only for type 1 (near zero number density) + call terminate_cohorts(currentSite, currentPatch,1,24,bc_in) call fuse_cohorts(currentSite, currentPatch, bc_in) end if @@ -590,22 +596,10 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) end do end if comp_excl_type - ! Check to make sure the changes are within bounds - do ic = 1,n_layer - cohort => layer_co(ic)%p - if( ((layer_co(ic)%pd_area - cohort%c_area) > co_area_target_precision ) .or. & - (layer_co(ic)%pd_area < 0._r8) ) then - write(fates_log(),*) 'negative,or more area than the cohort has is being promoted/demoted' - write(fates_log(),*) 'change: ',layer_co(ic)%pd_area - write(fates_log(),*) 'existing area:',cohort%c_area - write(fates_log(),*) 'excess: ',layer_co(ic)%pd_area - cohort%c_area - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end do - ! Part 3: ! Apply the area changes by splitting the cohort and re-assigning ! either all or part of it to a new layer + ! Check to make sure the changes are within bounds ic_loop0: do ic = 1,n_layer @@ -617,26 +611,31 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! and not trivialy small (larger than precision ! check), then split it and move part of it ! If the dem/prom area is less than zero or larger than - ! the cohort area within precision checks then - ! we would have failed in the previous checks + ! the cohort area within precision checks then fail + + + whole_or_part: if( ((layer_co(ic)%pd_area - cohort%c_area) > co_area_target_precision ) .or. & + (layer_co(ic)%pd_area < 0._r8) ) then + write(fates_log(),*) 'negative,or more area than the cohort has is being promoted/demoted' + write(fates_log(),*) 'change: ',layer_co(ic)%pd_area + write(fates_log(),*) 'existing area:',cohort%c_area + write(fates_log(),*) 'excess: ',layer_co(ic)%pd_area - cohort%c_area + call endrun(msg=errMsg(sourcefile, __LINE__)) - whole_or_part: if ( abs(layer_co(ic)%pd_area - cohort%c_area) < & - co_area_target_precision ) then + + elseif ( abs(layer_co(ic)%pd_area - cohort%c_area) < co_area_target_precision ) then ! Whole cohort promotion/demotion cohort%canopy_layer = cohort%canopy_layer + ilyr_change - - elseif( (layer_co(ic)%pd_area < cohort%c_area) .and. & - (layer_co(ic)%pd_area > 0 ) ) then + + elseif( layer_co(ic)%pd_area > 0._r8 ) then ! Partial cohort promotion/demotion - ! Make a copy of the current cohort. The copy and the original ! conserve total number density. The copy ! remains in the upper-story. The original is the one ! demoted to the understory - allocate(copyc) ! (keep as an example) @@ -660,7 +659,7 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) call copyc%InitPRTBoundaryConditions() remainder_area = cohort%c_area - layer_co(ic)%pd_area - copyc%n = cohort%n*remainder_area/cohort%c_area + copyc%n = cohort%n*min(1._r8,max(0._r8,remainder_area/cohort%c_area)) cohort%n = cohort%n - copyc%n ! The copied cohort is the part that remains in-layer @@ -692,24 +691,26 @@ subroutine PromoteOrDemote(site,patch,target_layer,phase,target_area) ! Part 4: ! keep track of number and biomass promoted/demoted - leaf_c = cohort%prt%GetState(leaf_organ,carbon12_element) - store_c = cohort%prt%GetState(store_organ,carbon12_element) - fnrt_c = cohort%prt%GetState(fnrt_organ,carbon12_element) - sapw_c = cohort%prt%GetState(sapw_organ,carbon12_element) - struct_c = cohort%prt%GetState(struct_organ,carbon12_element) - - if(phase==demotion_phase) then - site%demotion_rate(cohort%size_class) = & - site%demotion_rate(cohort%size_class) + cohort%n - site%demotion_carbonflux = site%demotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n - else - site%promotion_rate(cohort%size_class) = & - site%promotion_rate(cohort%size_class) + cohort%n - site%promotion_carbonflux = site%promotion_carbonflux + & - (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + if( layer_co(ic)%pd_area > 0._r8 ) then + leaf_c = cohort%prt%GetState(leaf_organ,carbon12_element) + store_c = cohort%prt%GetState(store_organ,carbon12_element) + fnrt_c = cohort%prt%GetState(fnrt_organ,carbon12_element) + sapw_c = cohort%prt%GetState(sapw_organ,carbon12_element) + struct_c = cohort%prt%GetState(struct_organ,carbon12_element) + + if(phase==demotion_phase) then + site%demotion_rate(cohort%size_class) = & + site%demotion_rate(cohort%size_class) + cohort%n + site%demotion_carbonflux = site%demotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + else + site%promotion_rate(cohort%size_class) = & + site%promotion_rate(cohort%size_class) + cohort%n + site%promotion_carbonflux = site%promotion_carbonflux + & + (leaf_c + store_c + fnrt_c + sapw_c + struct_c) * cohort%n + end if end if - + end do ic_loop0 end associate diff --git a/biogeochem/FatesCohortMod.F90 b/biogeochem/FatesCohortMod.F90 index 84ad0f2247..15195e4d0f 100644 --- a/biogeochem/FatesCohortMod.F90 +++ b/biogeochem/FatesCohortMod.F90 @@ -262,7 +262,6 @@ module FatesCohortMod real(r8) :: dndt ! time derivative of cohort size [n/year] real(r8) :: dhdt ! time derivative of height [m/year] real(r8) :: ddbhdt ! time derivative of dbh [cm/year] - real(r8) :: dbdeaddt ! time derivative of dead biomass [kgC/year] !--------------------------------------------------------------------------- @@ -451,7 +450,6 @@ subroutine NanValues(this) this%dndt = nan this%dhdt = nan this%ddbhdt = nan - this%dbdeaddt = nan ! FIRE this%fraction_crown_burned = nan @@ -794,7 +792,6 @@ subroutine Copy(this, copyCohort) copyCohort%dndt = this%dndt copyCohort%dhdt = this%dhdt copyCohort%ddbhdt = this%ddbhdt - copyCohort%dbdeaddt = this%dbdeaddt ! FIRE copyCohort%fraction_crown_burned = this%fraction_crown_burned @@ -1140,7 +1137,6 @@ subroutine Dump(this) write(fates_log(),*) 'cohort%dndt = ', this%dndt write(fates_log(),*) 'cohort%dhdt = ', this%dhdt write(fates_log(),*) 'cohort%ddbhdt = ', this%ddbhdt - write(fates_log(),*) 'cohort%dbdeaddt = ', this%dbdeaddt write(fates_log(),*) 'cohort%fraction_crown_burned = ', this%fraction_crown_burned write(fates_log(),*) 'cohort%fire_mort = ', this%fire_mort write(fates_log(),*) 'cohort%crownfire_mort = ', this%crownfire_mort diff --git a/main/FatesUtilsMod.F90 b/main/FatesUtilsMod.F90 index 03537bd226..db0cc7fa11 100644 --- a/main/FatesUtilsMod.F90 +++ b/main/FatesUtilsMod.F90 @@ -76,7 +76,6 @@ subroutine check_var_real(r8_var, var_name, return_code) real(r8), parameter :: r8_type = 1.0 real(r8), parameter :: overflow = huge(r8_type) - real(r8), parameter :: underflow = tiny(r8_type) return_code = 0 @@ -92,13 +91,6 @@ subroutine check_var_real(r8_var, var_name, return_code) return_code = return_code + 10 end if - ! Underflow check (within 100x of min precision) - if (abs(r8_var) < 100.0_r8*underflow) then - write(fates_log(),*) 'Nigh underflow detected, ',trim(var_name),': ',r8_var - return_code = return_code + 100 - end if - - end subroutine check_var_real !==========================================================================================! From f7fa74d76fa804d9b98b94b4582f54327d8a387d Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Mon, 17 Nov 2025 17:21:01 -0800 Subject: [PATCH 182/194] removed unnecessary call to terminate --- biogeochem/EDCanopyStructureMod.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index 463a5712de..98193ebd2d 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -206,7 +206,6 @@ subroutine canopy_structure( currentSite , bc_in ) ! Terminate cohorts before organizing canopy. That ! step will be interested in preserving area, so termination ! during that step will be counter productive - call terminate_cohorts(currentSite, currentPatch, -1,13,bc_in) call terminate_cohorts(currentSite, currentPatch, 1,13,bc_in) call terminate_cohorts(currentSite, currentPatch, 2,13,bc_in) From d41f9ecbb21e3e6fcad006bd2f77769fb3c03d4e Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Mon, 24 Nov 2025 11:16:56 +0100 Subject: [PATCH 183/194] update paramfilename --- parameter_files/fates_params_default.cdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index 5fa60ee1df..a3a16f945e 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -1,4 +1,4 @@ -netcdf fates_params_sci.1.85.1_api.40.0.0_14pft_nor_sci1_api1_default { +netcdf fates_params_sci.1.88.3_api.42.0.0_14pft_nor_sci1_api1_default { dimensions: fates_NCWD = 4 ; fates_history_age_bins = 7 ; From 24c40f614ba5cf3e52d12d908072516326c73d88 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Tue, 25 Nov 2025 15:38:15 +0100 Subject: [PATCH 184/194] fix compile errors. --- biogeochem/EDPatchDynamicsMod.F90 | 25 +++++++++---------------- biogeochem/EDPhysiologyMod.F90 | 6 +----- main/EDMainMod.F90 | 3 +-- main/FatesInterfaceTypesMod.F90 | 6 +++--- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 41575f745f..204d17dc72 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -509,7 +509,6 @@ subroutine spawn_patches( currentSite, bc_in ) ! !ARGUMENTS: type (ed_site_type), intent(inout) :: currentSite type (bc_in_type), intent(in) :: bc_in - type (bc_out_type), intent(inout) :: bc_out ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: newPatch @@ -568,8 +567,6 @@ subroutine spawn_patches( currentSite, bc_in ) ! zero the diagnostic disturbance rate fields currentSite%disturbance_rates(:,:,:) = 0._r8 - bc_out%fire_closs_to_atm_si = 0._r8 - bc_out%grazing_closs_to_atm_si = 0._r8 ! get rules for vegetation clearing during land use change call GetLanduseChangeRules(clearing_matrix) @@ -756,7 +753,7 @@ subroutine spawn_patches( currentSite, bc_in ) call CopyPatchMeansTimers(currentPatch, newPatch) - call TransLitterNewPatch( currentSite, currentPatch, newPatch, patch_site_areadis, bc_out, i_disturbance_type) + call TransLitterNewPatch( currentSite, currentPatch, newPatch, patch_site_areadis, i_disturbance_type) ! Transfer in litter fluxes from plants in various contexts of death and destruction @@ -772,13 +769,13 @@ subroutine spawn_patches( currentSite, bc_in ) end if case (dtype_ifire) call fire_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis,bc_in, bc_out) + newPatch, patch_site_areadis,bc_in) case (dtype_ifall) call mortality_litter_fluxes(currentSite, currentPatch, & newPatch, patch_site_areadis,bc_in) case (dtype_ilandusechange) call landusechange_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis,bc_in, bc_out, & + newPatch, patch_site_areadis,bc_in, & clearing_matrix(i_donorpatch_landuse_type,i_landusechange_receiverpatchlabel)) ! if land use change, then may need to change nocomp pft, so tag as having transitioned LU @@ -1498,7 +1495,7 @@ subroutine spawn_patches( currentSite, bc_in ) allocate(temp_patch) - call split_patch(currentSite, currentPatch, temp_patch, fraction_to_keep, newp_area, bc_out) + call split_patch(currentSite, currentPatch, temp_patch, fraction_to_keep, newp_area) ! temp_patch%nocomp_pft_label = 0 @@ -1601,7 +1598,7 @@ subroutine spawn_patches( currentSite, bc_in ) ! split buffer patch in two, keeping the smaller buffer patch to put into new patches allocate(temp_patch) - call split_patch(currentSite, buffer_patch, temp_patch, fraction_to_keep, newp_area, bc_out) + call split_patch(currentSite, buffer_patch, temp_patch, fraction_to_keep, newp_area) ! give the new patch the intended nocomp PFT label temp_patch%nocomp_pft_label = i_pft @@ -1710,7 +1707,7 @@ end subroutine spawn_patches ! ----------------------------------------------------------------------------------------- - subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, area_to_remove, bc_out) + subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, area_to_remove) ! ! !DESCRIPTION: ! Split a patch into two patches that are identical except in their areas @@ -1721,7 +1718,6 @@ subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, a type(fates_patch_type) , intent(inout), pointer :: new_patch ! New Patch real(r8), intent(in) :: fraction_to_keep ! fraction of currentPatch to keep, the rest goes to newpatch real(r8), intent(in), optional :: area_to_remove ! area of currentPatch to remove, the rest goes to newpatch - type(bc_out_type) , intent(inout) :: bc_out ! ! !LOCAL VARIABLES: integer :: el ! element loop index @@ -1758,7 +1754,7 @@ subroutine split_patch(currentSite, currentPatch, new_patch, fraction_to_keep, a call CopyPatchMeansTimers(currentPatch, new_patch) - call TransLitterNewPatch( currentSite, currentPatch, new_patch, temp_area, bc_out, 0) + call TransLitterNewPatch( currentSite, currentPatch, new_patch, temp_area, 0) ! Next, we loop through the cohorts in the donor patch, copy them with @@ -1893,7 +1889,7 @@ end subroutine check_patch_area subroutine TransLitterNewPatch(currentSite, & currentPatch, & newPatch, & - patch_site_areadis, bc_out, dist_type) + patch_site_areadis, dist_type) ! ----------------------------------------------------------------------------------- ! @@ -1942,7 +1938,6 @@ subroutine TransLitterNewPatch(currentSite, & type(fates_patch_type) , intent(inout) :: newPatch ! New patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated ! by current patch - type(bc_out_type) , intent(inout) :: bc_out integer, intent(in) :: dist_type ! disturbance type ! locals @@ -2149,7 +2144,7 @@ end subroutine TransLitterNewPatch ! ============================================================================ subroutine fire_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis, bc_in, bc_out) + newPatch, patch_site_areadis, bc_in) ! ! !DESCRIPTION: ! CWD pool burned by a fire. @@ -2371,7 +2366,6 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & burned_mass = num_dead_trees * SF_val_CWD_frac_adj(c) * bstem * & currentCohort%fraction_crown_burned site_mass%burn_flux_to_atm = site_mass%burn_flux_to_atm + burned_mass - bc_out%fire_closs_to_atm_si = bc_out%fire_closs_to_atm_si + burned_mass * ha_per_m2 * days_per_sec endif new_litt%ag_cwd(c) = new_litt%ag_cwd(c) + donatable_mass * donate_m2 curr_litt%ag_cwd(c) = curr_litt%ag_cwd(c) + donatable_mass * retain_m2 @@ -2639,7 +2633,6 @@ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & type(fates_patch_type) , intent(inout), target :: newPatch ! New Patch real(r8) , intent(in) :: patch_site_areadis ! Area being donated type(bc_in_type) , intent(in) :: bc_in - type(bc_out_type) , intent(inout) :: bc_out logical , intent(in) :: clearing_matrix_element ! whether or not to clear vegetation ! diff --git a/biogeochem/EDPhysiologyMod.F90 b/biogeochem/EDPhysiologyMod.F90 index 992b20c061..af0eed295d 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -2803,7 +2803,7 @@ end subroutine recruitment ! ====================================================================================== - subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) + subroutine CWDInput( currentSite, currentPatch, litt, bc_in) ! ! !DESCRIPTION: @@ -2823,7 +2823,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) type(fates_patch_type),intent(inout), target :: currentPatch type(litter_type),intent(inout),target :: litt type(bc_in_type),intent(in) :: bc_in - type(bc_out_type),intent(inout) :: bc_out ! ! !LOCAL VARIABLES: @@ -2977,9 +2976,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) site_mass%herbivory_flux_out + & leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n - bc_out%grazing_closs_to_atm_si = bc_out%grazing_closs_to_atm_si + & - leaf_herbivory * (1._r8 - herbivory_element_use_efficiency) * currentCohort%n * & - ha_per_m2 * days_per_sec ! Assumption: turnover from deadwood and sapwood are lumped together in CWD pool diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 36ba05ce9b..81d0f2e49e 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -302,7 +302,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! make new patches from disturbed land if (do_patch_dynamics.eq.itrue ) then - call spawn_patches(currentSite, bc_in, bc_out) + call spawn_patches(currentSite, bc_in) call TotalBalanceCheck(currentSite,3) @@ -842,7 +842,6 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: currentPatch type(site_massbal_type), pointer :: site_cmass - real(r8) :: total_stock ! dummy variable for receiving from sitemassstock !----------------------------------------------------------------------- site_cmass => currentSite%mass_balance(element_pos(carbon12_element)) diff --git a/main/FatesInterfaceTypesMod.F90 b/main/FatesInterfaceTypesMod.F90 index 871669dafd..5b6839fc3d 100644 --- a/main/FatesInterfaceTypesMod.F90 +++ b/main/FatesInterfaceTypesMod.F90 @@ -805,9 +805,9 @@ module FatesInterfaceTypesMod real(r8) :: grazing_closs_to_atm_si ! Loss of carbon to atmosphere via grazing [Site-Level, kgC m-2 s-1] real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, kgC m-2 s-1] - ! direct carbon loss to atm pathways - real(r8) :: grazing_closs_to_atm_si ! Loss of carbon to atmosphere via grazing [Site-Level, gC m-2 s-1] - real(r8) :: fire_closs_to_atm_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] + ! non-accumulated fields to pass to the HLM that asks for co2 flux each timestep + real(r8) :: grazing_closs_to_atm_tstep_si ! Loss of carbon to atmosphere via grazing [Site-Level, gC m-2 s-1] + real(r8) :: fire_closs_to_atm_tstep_si ! Loss of carbon to atmosphere via burning (includes burning from land use change) [Site-Level, gC m-2 s-1] ! summary carbon stock variables real(r8) :: veg_c_si ! Total vegetation carbon [Site-Level, gC m-2] From 95c4bcbf335ffd301101ec38e8589cd63d5a4f5c Mon Sep 17 00:00:00 2001 From: Gregory Lemieux Date: Tue, 25 Nov 2025 17:01:07 -0800 Subject: [PATCH 185/194] move bc_out gpp and ar calculation earlier in the ed_update_site call --- main/EDMainMod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 8d75256e50..a12435c385 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -841,7 +841,9 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) call set_patchno(currentSite,.true.,1) end if - + ! Set gpp and ar bc outputs prior to zeroing the associate site carbon mass variables + bc_out%gpp_site = site_cmass%gpp_acc * area_inv * days_per_sec + bc_out%ar_site = site_cmass%aresp_acc * area_inv * days_per_sec if(hlm_use_sp.eq.ifalse .and. (.not.is_restarting))then call canopy_spread(currentSite) @@ -919,8 +921,6 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting ) bc_out%fire_closs_to_atm_si = site_cmass%burn_flux_to_atm * area_inv * days_per_sec bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * area_inv * days_per_sec - bc_out%gpp_site = site_cmass%gpp_acc * area_inv * days_per_sec - bc_out%ar_site = site_cmass%aresp_acc * area_inv * days_per_sec end subroutine ed_update_site From 759ed0085e4aed2a95d4249c25928b9950f94669 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 25 Nov 2025 20:16:53 -0500 Subject: [PATCH 186/194] Added is restarting arguments --- main/EDMainMod.F90 | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index a12435c385..bbc42e380f 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -198,7 +198,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) call ZeroBCOutCarbonFluxes(bc_out) ! Zero mass balance - call TotalBalanceCheck(currentSite, 0, is_restarting=.false.) + call TotalBalanceCheck(currentSite, 0) ! We do not allow phenology while in ST3 mode either, it is hypothetically ! possible to allow this, but we have not plugged in the litter fluxes @@ -263,7 +263,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) currentPatch => currentPatch%younger enddo - call TotalBalanceCheck(currentSite,1,is_restarting=.false.) + call TotalBalanceCheck(currentSite,1) currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) @@ -286,7 +286,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) end if - call TotalBalanceCheck(currentSite,2,is_restarting=.false.) + call TotalBalanceCheck(currentSite,2) !********************************************************************************* ! Patch dynamics sub-routines: fusion, new patch creation (spwaning), termination. @@ -304,7 +304,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) call spawn_patches(currentSite, bc_in) - call TotalBalanceCheck(currentSite,3,is_restarting=.false.) + call TotalBalanceCheck(currentSite,3) ! fuse on the spawned patches. call fuse_patches(currentSite, bc_in ) @@ -319,14 +319,14 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) end if ! SP has changes in leaf carbon but we don't expect them to be in balance. - call TotalBalanceCheck(currentSite,4,is_restarting=.false.) + call TotalBalanceCheck(currentSite,4) ! kill patches that are too small call terminate_patches(currentSite, bc_in) end if ! Final instantaneous mass balance check - call TotalBalanceCheck(currentSite,5,is_restarting=.false.) + call TotalBalanceCheck(currentSite,5) end subroutine ed_ecosystem_dynamics @@ -943,7 +943,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! !ARGUMENTS: type(ed_site_type) , intent(inout) :: currentSite integer , intent(in) :: call_index - logical , intent(in) :: is_restarting + logical,optional , intent(in) :: is_restarting_arg ! ! !LOCAL VARIABLES: @@ -964,7 +964,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) real(r8) :: store_m ! "" storage real(r8) :: struct_m ! "" structure real(r8) :: repro_m ! "" reproduction - + logical :: is_restarting ! is the model going through its restart init procedure? integer :: el ! loop counter for element types ! nb. There is no time associated with these variables @@ -979,6 +979,13 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) logical, parameter :: print_cohorts = .true. ! Set to true if you want ! to print cohort data ! upon fail (lots of text) + + if(present(is_restarting_arg))then + is_restarting = is_restarting_arg + else + is_restarting = .false. + end if + !----------------------------------------------------------------------- if(hlm_use_sp.eq.ifalse)then From ed117e68fdf74c16edf8b33b15dcc4ae5f8ea4e5 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Wed, 26 Nov 2025 20:23:44 +0100 Subject: [PATCH 187/194] fix arg processing --- main/EDMainMod.F90 | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index bbc42e380f..df21329d30 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -943,7 +943,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! !ARGUMENTS: type(ed_site_type) , intent(inout) :: currentSite integer , intent(in) :: call_index - logical,optional , intent(in) :: is_restarting_arg + logical,optional , intent(in) :: is_restarting ! is the model going through its restart init procedure? ! ! !LOCAL VARIABLES: @@ -964,7 +964,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) real(r8) :: store_m ! "" storage real(r8) :: struct_m ! "" structure real(r8) :: repro_m ! "" reproduction - logical :: is_restarting ! is the model going through its restart init procedure? + logical :: l_is_restarting ! local version of the optional arg integer :: el ! loop counter for element types ! nb. There is no time associated with these variables @@ -979,11 +979,9 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) logical, parameter :: print_cohorts = .true. ! Set to true if you want ! to print cohort data ! upon fail (lots of text) - - if(present(is_restarting_arg))then - is_restarting = is_restarting_arg - else - is_restarting = .false. + l_is_restarting = .false. + if(present(is_restarting))then + l_is_restarting = is_restarting end if !----------------------------------------------------------------------- @@ -1001,7 +999,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) call SiteMassStock(currentSite,el,total_stock,biomass_stock,litter_stock,seed_stock) change_in_stock = total_stock - site_mass%old_stock - if(is_restarting) then + if(l_is_restarting) then flux_in = 0._r8 flux_out = 0._r8 else @@ -1118,7 +1116,7 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) ! This is the last check of the sequence, where we update our total ! error check and the final fates stock - if(call_index == final_check_id .and. .not.is_restarting) then + if(call_index == final_check_id .and. .not. l_is_restarting) then site_mass%old_stock = total_stock site_mass%err_fates = net_flux - change_in_stock end if From ea75fa574f1cc4ec2b4e47927fcc68c401f2b4e4 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 2 Dec 2025 07:54:27 -0700 Subject: [PATCH 188/194] fixed cohort prom/dem precision error to match math precision for large numbers --- biogeochem/EDCanopyStructureMod.F90 | 33 ++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index e21e1c56e7..c3acd6ea0a 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -76,15 +76,11 @@ module EDCanopyStructureMod character(len=255) :: smsg ! Message string for deallocation errors ! Precision targets for demotion and promotion - ! We have two: - ! "pa_area_target_precision" is the required precision at the patch level, - ! we keep shuffling and splitting cohorts until each layer is within this precision ! "co_area_target_precision" is the required precision at the cohort level, ! essentially it is the minimum amount of change required to not ignore ! a partial promotion or demotion - real(r8), parameter :: pa_area_target_precision = 1.0E-11_r8 - real(r8), parameter :: co_area_target_precision = 1.0E-12_r8 + real(r8), parameter :: co_area_target_precision = 1.0E-9_r8 integer, parameter :: demotion_phase = 1 integer, parameter :: promotion_phase = 2 @@ -94,8 +90,6 @@ module EDCanopyStructureMod real(r8), parameter :: area_check_precision = 1.0E-7_r8 ! Area conservation checks must ! be within this absolute tolerance - real(r8), parameter :: area_check_rel_precision = 1.0E-4_r8 ! Area conservation checks must - ! be within this relative tolerance real(r8), parameter :: similar_height_tol = 1.0E-3_r8 ! I think trees that differ by 1mm ! can be roughly considered the same right? @@ -169,7 +163,7 @@ subroutine canopy_structure( currentSite , bc_in ) integer :: i_lyr ! current layer index integer :: z ! Current number of canopy layers. (1= canopy, 2 = understorey) integer :: ipft - real(r8) :: arealayer(nclmax+5) ! Amount of plant area currently in each canopy layer + real(r8) :: arealayer!(nclmax+5) ! Amount of plant area currently in each canopy layer integer :: patch_area_counter ! count iterations used to solve canopy areas logical :: area_not_balanced ! logical controlling if the patch layer areas real(r8) :: target_area ! Canopy area that is either in excess/defiency @@ -182,7 +176,7 @@ subroutine canopy_structure( currentSite , bc_in ) ! try to re-balance 3 times. If that doesn't give layer areas ! within tolerance of canopy area, there is something wrong - integer, parameter :: max_patch_iterations = 10 + integer, parameter :: max_patch_iterations = nclmax + 7 !---------------------------------------------------------------------- @@ -243,8 +237,8 @@ subroutine canopy_structure( currentSite , bc_in ) z = NumCanopyLayers(currentPatch) do i_lyr = 1,z ! Loop around the currently occupied canopy layers. - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) - target_area = max(0._r8,arealayer(i_lyr) - (1._r8-imperfect_fraction)*currentPatch%area) + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) + target_area = max(0._r8,arealayer - (1._r8-imperfect_fraction)*currentPatch%area) call PromoteOrDemote(currentSite, currentPatch, i_lyr, demotion_phase, target_area) end do @@ -261,8 +255,8 @@ subroutine canopy_structure( currentSite , bc_in ) ! We only promote if we have at least two layers if (z>1) then do i_lyr=2,z - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr-1,arealayer(i_lyr-1)) - target_area = max(0._r8,(1._r8-imperfect_fraction)*currentPatch%area - arealayer(i_lyr-1)) + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr-1,arealayer) + target_area = max(0._r8,(1._r8-imperfect_fraction)*currentPatch%area - arealayer) call PromoteOrDemote(currentSite, currentPatch, i_lyr, promotion_phase, target_area) end do @@ -281,14 +275,14 @@ subroutine canopy_structure( currentSite , bc_in ) z = NumCanopyLayers(currentPatch) area_not_balanced = .false. - do i_lyr = 1,z - call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer(i_lyr)) + do i_lyr = 1,min(z,nclmax) + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) if(i_lyr < z)then - if (abs(arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + if (abs(arealayer-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then area_not_balanced = .true. end if else - if ((arealayer(i_lyr)-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then + if ((arealayer-(1._r8-imperfect_fraction)*currentPatch%area) > area_check_precision) then area_not_balanced = .true. end if end if @@ -308,8 +302,9 @@ subroutine canopy_structure( currentSite , bc_in ) write(fates_log(),*) 'spread:',currentSite%spread do i_lyr = 1,z write(fates_log(),*) '-----------------------------------------' - write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer(i_lyr) - write(fates_log(),*) 'bias [m2] (layer-patch): ',(arealayer(i_lyr)- & + call CanopyLayerArea(currentPatch,currentSite%spread,i_lyr,arealayer) + write(fates_log(),*) 'layer: ',i_lyr,' area: ',arealayer + write(fates_log(),*) 'bias [m2] (layer-patch): ',(arealayer - & (1._r8-imperfect_fraction)*currentPatch%area) currentCohort => currentPatch%tallest do while (associated(currentCohort)) From 8c12a4867bef716ef606a1368a658239c847dc05 Mon Sep 17 00:00:00 2001 From: Ryan Knox Date: Tue, 2 Dec 2025 10:58:54 -0700 Subject: [PATCH 189/194] wrapping a warning message inside FATESWarn to reduce logging --- biogeochem/EDCanopyStructureMod.F90 | 7 +++++-- biogeochem/EDPatchDynamicsMod.F90 | 11 ++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/biogeochem/EDCanopyStructureMod.F90 b/biogeochem/EDCanopyStructureMod.F90 index dbfd1d5051..d6ae9f52c8 100644 --- a/biogeochem/EDCanopyStructureMod.F90 +++ b/biogeochem/EDCanopyStructureMod.F90 @@ -78,7 +78,10 @@ module EDCanopyStructureMod ! Precision targets for demotion and promotion ! "co_area_target_precision" is the required precision at the cohort level, ! essentially it is the minimum amount of change required to not ignore - ! a partial promotion or demotion + ! a partial promotion or demotion. This number was chosen because + ! math precision is around 15 digits in fortran r8s, with numbers that + ! can get to magnitude e4, this gives us about two orders of magitude in math + ! precision (ie e15-(e4+e9)=e2) in significant digits to match this absolute precision real(r8), parameter :: co_area_target_precision = 1.0E-9_r8 @@ -163,7 +166,7 @@ subroutine canopy_structure( currentSite , bc_in ) integer :: i_lyr ! current layer index integer :: z ! Current number of canopy layers. (1= canopy, 2 = understorey) integer :: ipft - real(r8) :: arealayer!(nclmax+5) ! Amount of plant area currently in each canopy layer + real(r8) :: arealayer ! Amount of plant area currently in each canopy layer integer :: patch_area_counter ! count iterations used to solve canopy areas logical :: area_not_balanced ! logical controlling if the patch layer areas real(r8) :: target_area ! Canopy area that is either in excess/defiency diff --git a/biogeochem/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 6a4fa1034a..db573208e6 100644 --- a/biogeochem/EDPatchDynamicsMod.F90 +++ b/biogeochem/EDPatchDynamicsMod.F90 @@ -3,7 +3,7 @@ module EDPatchDynamicsMod ! Controls formation, creation, fusing and termination of patch level processes. ! ============================================================================ use FatesGlobals , only : fates_log - use FatesGlobals , only : FatesWarn,N2S,A2S + use FatesGlobals , only : FatesWarn,N2S,A2S,I2S use FatesInterfaceTypesMod, only : hlm_freq_day use FatesInterfaceTypesMod, only : hlm_current_tod use EDPftvarcon , only : EDPftvarcon_inst @@ -3381,6 +3381,7 @@ subroutine terminate_patches(currentSite, bc_in) real(r8) areatot ! variable for checking whether the total patch area is wrong. real(r8) :: state_vector_driver(n_landuse_cats) ! [m2/m2] real(r8) :: state_vector_internal(n_landuse_cats) ! [m2/m2] + character(len=1024) :: warn_msg ! for defining a warning message !--------------------------------------------------------------------- ! Initialize the count cycles @@ -3411,8 +3412,12 @@ subroutine terminate_patches(currentSite, bc_in) if ( .not. gotfused ) then !! somehow didn't find a patch to fuse with. - write(fates_log(),*) 'Warning. small nocomp patch wasnt able to find another patch to fuse with.', & - currentPatch%nocomp_pft_label, currentPatch%land_use_label, currentPatch%area + warn_msg = 'small nocomp patch wasnt able to find '// & + 'another patch to fuse with. '// & + 'nocomp pft: '//trim(I2S(currentPatch%nocomp_pft_label))// & + 'lu label: '//trim(I2S(currentPatch%land_use_label))// & + 'area: '//trim(N2S(currentPatch%area)) + call FatesWarn(warn_msg,index=5) endif else nocomp_if From 77915835f4f701b9663e82d389d722d9d996bb5f Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Wed, 3 Dec 2025 15:57:10 +0100 Subject: [PATCH 190/194] fix merge --- main/EDMainMod.F90 | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index 2f87e0a168..991d4e8318 100644 --- a/main/EDMainMod.F90 +++ b/main/EDMainMod.F90 @@ -1005,24 +1005,6 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) call SiteMassStock(currentSite,el,total_stock,biomass_stock,litter_stock,seed_stock) change_in_stock = total_stock - site_mass%old_stock -<<<<<<< HEAD - - flux_in = site_mass%seed_in + & - site_mass%net_root_uptake + & - site_mass%gpp_acc + & - site_mass%flux_generic_in + & - site_mass%patch_resize_err - - flux_out = sum(site_mass%wood_product_harvest(:)) + & - sum(site_mass%wood_product_landusechange(:)) + & - site_mass%burn_flux_to_atm + & - site_mass%seed_out + & - site_mass%flux_generic_out + & - site_mass%frag_out + & - site_mass%aresp_acc + & - site_mass%herbivory_flux_out - -======= if(l_is_restarting) then flux_in = 0._r8 flux_out = 0._r8 @@ -1043,7 +1025,6 @@ subroutine TotalBalanceCheck (currentSite, call_index, is_restarting ) site_mass%herbivory_flux_out end if ->>>>>>> upstream/main net_flux = flux_in - flux_out error = abs(net_flux - change_in_stock) From 62aa8ef14d95823fd39326dc627cab5ba295e9d3 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Wed, 3 Dec 2025 20:27:05 +0100 Subject: [PATCH 191/194] revert fullfates recruit init density. --- parameter_files/fates_params_default.cdl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index e7311ee1a6..a2c3429b23 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -1572,7 +1572,8 @@ fates_mort_bmort = 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, fates_recruit_height_min = 1.3, 1.3, 1.3, 1.3, 1.3, 1.3, 0.2, 0.2, 0.2, 0.1, 0.1, 0.1, 0.1, 0.1 ; - fates_recruit_init_density_full_fates = 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 5, 2, 2 ; + fates_recruit_init_density_full_fates = 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, + 0.16, 0.2, 0.2, 0.2, 0.2 ; fates_recruit_init_nocomp = -1, -1, -1, -1, -1, -1, -0.5, -0.5, -0.5, -0.5, -0.5, -0.1, -0.1, -0.1 ; From 9a4e1f8755e94da981c5a0cab7062b59f0f407a9 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Thu, 4 Dec 2025 09:27:42 +0100 Subject: [PATCH 192/194] fix indents --- parameter_files/fates_params_default.cdl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index a2c3429b23..e31421d74d 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -549,15 +549,15 @@ variables: double fates_recruit_height_min(fates_pft) ; fates_recruit_height_min:units = "m" ; fates_recruit_height_min:long_name = "the minimum height (ie starting height) of a newly recruited plant" ; - double fates_recruit_init_density_full_fates(fates_pft) ; - fates_recruit_init_density_full_fates:units = "stems/m2" ; - fates_recruit_init_density_full_fates:long_name = "initial seedling density for a cold-start near-bare-ground simulation in full fates (with competition)" ; -double fates_recruit_init_nocomp(fates_pft) ; + double fates_recruit_init_density_full_fates(fates_pft) ; + fates_recruit_init_density_full_fates:units = "stems/m2" ; + fates_recruit_init_density_full_fates:long_name = "initial seedling density for a cold-start near-bare-ground simulation in full fates (with competition)" ; + double fates_recruit_init_nocomp(fates_pft) ; fates_recruit_init_nocomp:units = "stems/m2 or dbh cm"; fates_recruit_init_nocomp:long_name = "initial seedling density or initial size of seedlings (if negative) in nocomp mode"; double fates_init_seed(fates_pft) ; - fates_init_seed:units = "kg/m2" ; - fates_init_seed:long_name = "initial seed pool (only applied)" ; + fates_init_seed:units = "kg/m2" ; + fates_init_seed:long_name = "initial seed pool (only applied)" ; double fates_recruit_prescribed_rate(fates_pft) ; fates_recruit_prescribed_rate:units = "n/yr" ; fates_recruit_prescribed_rate:long_name = "recruitment rate for prescribed physiology mode" ; From b46a74315f309df014695f126d91fbed58218ce5 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Fri, 5 Dec 2025 14:47:56 +0100 Subject: [PATCH 193/194] fix rx-fire variables for ERS-ERI tests --- fire/SFMainMod.F90 | 3 +++ main/EDInitMod.F90 | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/fire/SFMainMod.F90 b/fire/SFMainMod.F90 index 7e6de3323b..25c942257e 100644 --- a/fire/SFMainMod.F90 +++ b/fire/SFMainMod.F90 @@ -373,6 +373,9 @@ subroutine CalculateSurfaceFireIntensity(currentSite) logical :: fi_check ! is (potential) fire intensity high enough for fire to actually happen? logical :: has_ignition ! is ignition greater than zero? + + currentSite%rxfire_area_fuel = 0.0_r8 + currentSite%rxfire_area_fi = 0.0_r8 currentPatch => currentSite%oldest_patch do while (associated(currentPatch)) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 60f6892d70..04bcf28fb4 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -417,6 +417,12 @@ subroutine zero_site( site_in ) site_in%resources_management%harvest_debt = 0.0_r8 site_in%resources_management%harvest_debt_sec = 0.0_r8 + ! rxfire vars: + + site_in%rxfire_area_fuel = 0.0_r8 + site_in%rxfire_area_fi = 0.0_r8 + site_in%rxfire_area_final = 0.0_r8 + ! canopy spread site_in%spread = 0._r8 From 6adf1d1bbab13d1a0b963e39a830a28a032021d2 Mon Sep 17 00:00:00 2001 From: mvdebolskiy Date: Fri, 5 Dec 2025 19:08:22 +0100 Subject: [PATCH 194/194] update paramfile tag --- parameter_files/fates_params_default.cdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameter_files/fates_params_default.cdl b/parameter_files/fates_params_default.cdl index e31421d74d..f66acd6b71 100644 --- a/parameter_files/fates_params_default.cdl +++ b/parameter_files/fates_params_default.cdl @@ -1,4 +1,4 @@ -netcdf fates_params_sci.1.88.3_api.42.0.0_14pft_nor_sci1_api1_default { +netcdf fates_params_sci.1.88.6_api.42.0.0_14pft_nor_sci1_api1_default { dimensions: fates_NCWD = 4 ; fates_history_age_bins = 7 ;