Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b78890c
Add NVP parameters needed in FATES.
huitang-earth Apr 13, 2026
b068a95
Add NVP parameters into parameter json file.
huitang-earth Apr 13, 2026
01a0edb
Add NVP allocation subroutine for converting between LAI-LEAFC-NVP la…
huitang-earth Apr 13, 2026
576eba8
Add NVP cohort creation and patch-level canopy update.
huitang-earth Apr 13, 2026
50f1586
Add NVP input and output variables with the host model.
huitang-earth Apr 13, 2026
2a347c1
Add initialization of NVP input and output variables with the host mo…
huitang-earth Apr 13, 2026
8620ae4
Add two options for calculating NVP radiation absorption.
huitang-earth Apr 13, 2026
e06c471
Add NVP absorbed radition for photosynthesis (both snow and no-snow c…
huitang-earth Apr 13, 2026
84eef56
Exclude NVP contribution from LAI sent to the host model.
huitang-earth Apr 13, 2026
47b0171
Add NVP photosynthesis (no root, no stomatal conductance).
huitang-earth Apr 13, 2026
4294059
Add NVP history output fields.
huitang-earth Apr 13, 2026
86e3864
Add NVP restart fields.
huitang-earth Apr 13, 2026
d5262c1
Add a new root profile mode (no root) and set root_fraction to zero f…
huitang-earth Apr 14, 2026
af80f03
Add NVP as a pft in the default parameter file.
huitang-earth Apr 21, 2026
2d93166
Change NVP to non-vascular phototroph.
huitang-earth Apr 21, 2026
4b59973
Fixing bugs during compiling phase, no scientific changes.
huitang-earth Apr 26, 2026
9bad8c9
correct the hlm_pft mapping for arctic grass (12).
huitang-earth Apr 30, 2026
49c247b
Add alternative parameter file for moss, assuming arctic grass (12) o…
huitang-earth Apr 30, 2026
4ff1c57
Improve the comments on the NVP code in FatesRadiationDriveMod.F90
huitang-earth May 12, 2026
114449f
add initial values for bc_in%rb_pa(:) for NVP only patch.
huitang-earth May 12, 2026
57bbb72
Skip bare-ground patch when aggregate nvp variables to site level.
huitang-earth May 12, 2026
b267a73
Avoid unconditional minimum elai and esai for NVP.
huitang-earth May 12, 2026
6686502
Add initialization of optical parameter nvp_omega-pa for nvp run.
huitang-earth May 25, 2026
06fe7fa
Add debugging output for GPP of NVP.
huitang-earth Jun 1, 2026
308bb5a
Correct NVP's SP mode to assign proper nvp_dz.
huitang-earth Jun 1, 2026
6791300
Add separate bc_out aggregation for the SP mode of NVP.
huitang-earth Jun 1, 2026
80fc808
Correct the partition of sunlit and sunshade lai of NVP for photosynt…
huitang-earth Jun 1, 2026
aaef893
Allow photosynthesis module to see NVP lai rather than zero.
huitang-earth Jun 1, 2026
54cd2c3
Set btran_eff for NVP to 1.0, no soil water stress.
huitang-earth Jun 1, 2026
4420e8a
modify sla and bd parameter to yield a reasonable nvp depth.
huitang-earth Jun 11, 2026
777510c
Add dry and wet albedo for nvp (can be further improved).
huitang-earth Jun 11, 2026
3f9d00c
Consider dry and wet albedo for nvp in radiation scheme.
huitang-earth Jun 11, 2026
7b9770a
Revise the structure of ground albedo blend to be done in CLM. FATES …
huitang-earth Jun 14, 2026
7ce2439
Move nvp ci equation into CiFunc
huitang-earth Jun 17, 2026
9716bd9
FatesSunShadeFracs: Use site's coszen, not bc_in's.
samsrabin Jun 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 162 additions & 37 deletions biogeochem/EDCanopyStructureMod.F90

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions biogeochem/EDPhysiologyMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ module EDPhysiologyMod
use FatesAllometryMod , only : carea_allom
use FatesAllometryMod , only : CheckIntegratedAllometries
use FatesAllometryMod, only : set_root_fraction
use FatesAllometryMod, only : NVP_allom ! [PORTED by Hui Tang: NVP SP mode]
use FatesInterfaceTypesMod, only : hlm_use_nvp ! [PORTED by Hui Tang: NVP SP mode]
use LeafBiophysicsMod, only : lb_params ! [PORTED by Hui Tang: NVP PFT identification]
use PRTGenericMod, only : prt_carbon_allom_hyp
use PRTGenericMod, only : prt_cnp_flex_allom_hyp
use PRTGenericMod, only : prt_vartypes
Expand Down Expand Up @@ -1993,6 +1996,7 @@ subroutine assign_cohort_SP_properties(currentCohort, htop, tlai, tsai, parea, i
real(r8) :: dbh ! cohort dbh [cm]
real(r8) :: cohort_n ! cohort density [/m2]
real(r8) :: c_area ! cohort canopy area [m2]
real(r8) :: nvp_dz ! [PORTED by Hui Tang: NVP layer thickness from NVP allometry]

if (associated(currentCohort%shorter)) then
write(fates_log(),*) 'SP mode has >1 cohort'
Expand Down Expand Up @@ -2020,6 +2024,18 @@ subroutine assign_cohort_SP_properties(currentCohort, htop, tlai, tsai, parea, i
currentCohort%treelai = tlai
currentCohort%treesai = tsai

! [PORTED by Hui Tang: NVP SP mode — override leaf_c, c_area, nvp_dz using NVP allometry]
! In SP mode, calculate_SP_properties uses tree allometry (leafc_from_treelai) for all PFTs.
! For NVP, we must instead use NVP allometry: given prescribed treelai, compute the
! consistent leaf_c and nvp_dz. c_area = parea so nvp_frac_pa = parea/AREA = patch fraction.
if (hlm_use_nvp == itrue) then
if (lb_params%stomatal_intercept(currentCohort%pft) <= nearzero) then
currentCohort%c_area = parea
call NVP_allom(currentCohort%treelai, leaf_c, nvp_dz, inverse=.true.)
currentCohort%nvp_dz = nvp_dz
end if
end if

if (init .eq. ifalse) then
call SetState(currentCohort%prt, leaf_organ, carbon12_element, leaf_c, 1)
endif
Expand Down
73 changes: 68 additions & 5 deletions biogeochem/FatesAllometryMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ module FatesAllometryMod
use FatesGlobals , only : endrun => fates_endrun
use FatesGlobals , only : FatesWarn,N2S,A2S,I2S
use EDParamsMod , only : nlevleaf,dinc_vai,dlower_vai
! [PORTED by Hui Tang: NVP allometry parameters from FATES parameter file]
use EDParamsMod , only : nvp_bulk_density, nvp_sla
use DamageMainMod , only : GetCrownReduction
use LeafBiophysicsMod, only : DecayCoeffVcmax

Expand All @@ -124,6 +126,8 @@ module FatesAllometryMod
public :: set_root_fraction ! Generic wrapper to calculate normalized
! root profiles
public :: leafc_from_treelai ! Calculate target leaf carbon for a given treelai for SP mode
! [PORTED by Hui Tang: NVP (moss/lichen) bidirectional LAI<->biomass and LAI->thickness conversion]
public :: NVP_allom
public :: VegAreaLayer

public :: tree_lai_sai ! LAI and SAI calculations must work together, thus they
Expand Down Expand Up @@ -2800,6 +2804,8 @@ subroutine set_root_fraction(root_fraction, ft, zi, max_nlevroot)
integer, parameter :: jackson_beta_profile_type = 1
integer, parameter :: exponential_1p_profile_type = 2
integer, parameter :: exponential_2p_profile_type = 3
! [PORTED by Hui Tang: mode 4 = NVP (no soil roots); root_fraction stays all-zero]
integer, parameter :: no_root_profile_type = 4

integer :: root_profile_type
integer :: corr_id(1) ! This is the bin with largest fraction
Expand Down Expand Up @@ -2835,9 +2841,12 @@ subroutine set_root_fraction(root_fraction, ft, zi, max_nlevroot)
call exponential_1p_root_profile(root_fraction(1:nlevroot), zi(0:nlevroot), prt_params%fnrt_prof_a(ft))
case ( jackson_beta_profile_type )
call jackson_beta_root_profile(root_fraction(1:nlevroot), zi(0:nlevroot), prt_params%fnrt_prof_a(ft))
case ( exponential_2p_profile_type )
call exponential_2p_root_profile(root_fraction(1:nlevroot), zi(0:nlevroot), &
case ( exponential_2p_profile_type )
call exponential_2p_root_profile(root_fraction(1:nlevroot), zi(0:nlevroot), &
prt_params%fnrt_prof_a(ft),prt_params%fnrt_prof_b(ft))
! [PORTED by Hui Tang: NVP has no soil roots; root_fraction already zeroed above]
case ( no_root_profile_type )
! No root profile: root_fraction(:) = 0._r8 (initialized at line 2827, no further action needed)

case default
write(fates_log(),*) 'An undefined root profile type was specified'
Expand All @@ -2846,9 +2855,12 @@ subroutine set_root_fraction(root_fraction, ft, zi, max_nlevroot)
end select


correction = 1._r8 - sum(root_fraction)
corr_id = maxloc(root_fraction)
root_fraction(corr_id(1)) = root_fraction(corr_id(1)) + correction
! [PORTED by Hui Tang: skip normalization for no_root_profile_type; all zeros is intentional]
if (nint(prt_params%fnrt_prof_mode(ft)) /= no_root_profile_type) then
correction = 1._r8 - sum(root_fraction)
corr_id = maxloc(root_fraction)
root_fraction(corr_id(1)) = root_fraction(corr_id(1)) + correction
end if



Expand Down Expand Up @@ -3544,6 +3556,57 @@ subroutine size2dbh(size,ipft,dbh,dbh_maxh)
end subroutine size2dbh
! ============================================================================

! [PORTED by Hui Tang: NVP (moss/lichen) allometry - LAI<->biomass and LAI->layer thickness]
subroutine NVP_allom(lai, leaf_c, nvp_dz, inverse)
!
! !DESCRIPTION:
! Bidirectional conversion between NVP leaf carbon and LAI, and diagnosis
! of the NVP layer thickness from LAI.
!
! Parameters (from Porada et al. 2013 and related literature, read from FATES parameter file):
! nvp_sla [m2 g-1] specific leaf area of NVP
! nvp_bulk_density [kg m-3] NVP bulk density
! nv_c2b = 2.32 carbon-to-biomass ratio of NVP (kgC / kgDM)
! nv_t2l = 1.0 total-to-leaf biomass ratio (all biomass is leaf for NVP)
!
! When inverse = .false.: leaf_c -> lai, nvp_dz
! When inverse = .true. : lai -> leaf_c (nvp_dz still diagnosed from lai)
!
! nvp_dz = lai / (nvp_sla [m2 g-1] * 1e3 [g kg-1] * nvp_bulk_density [kg m-3])
! = lai / (nvp_sla * g_per_kg * nvp_bulk_density)
!
! !ARGUMENTS:
real(r8), intent(inout) :: lai ! NVP leaf area index [m2 leaf / m2 crown]
real(r8), intent(inout) :: leaf_c ! NVP leaf carbon per unit crown area [kgC m-2]
real(r8), intent(out) :: nvp_dz ! NVP layer thickness [m]
logical, intent(in) :: inverse ! .false. = leaf_c->lai; .true. = lai->leaf_c
!
! !LOCAL VARIABLES:
real(r8), parameter :: nv_c2b = 2.32_r8 ! carbon to dry-mass ratio [kgC kgDM-1]
real(r8), parameter :: nv_t2l = 1.0_r8 ! total-to-leaf biomass ratio [-]
!------------------------------------------------------------------------

if (inverse) then
! lai -> leaf_c
! leaf_mass_per_area [kgDM m-2] = lai [m2 m-2] / (nvp_sla [m2 g-1] * g_per_kg [g kg-1])
! leaf_c [kgC m-2] = leaf_mass_per_area * nv_c2b / nv_t2l
leaf_c = (lai / (nvp_sla * g_per_kg)) * nv_c2b / nv_t2l
else
! leaf_c -> lai
! leaf_mass [kgDM m-2] = leaf_c * nv_t2l / nv_c2b
! lai = leaf_mass * nvp_sla * g_per_kg
lai = (leaf_c * nv_t2l / nv_c2b) * nvp_sla * g_per_kg
end if

! Diagnose layer thickness from LAI regardless of direction
! nvp_dz [m] = lai [m2 m-2] / (nvp_sla [m2 g-1] * g_per_kg [g kg-1] * nvp_bulk_density [kg m-3])
if (nvp_bulk_density > nearzero) then
nvp_dz = lai / (nvp_sla * g_per_kg * nvp_bulk_density)
else
nvp_dz = 0.0_r8
end if

end subroutine NVP_allom
! ============================================================================

end module FatesAllometryMod
33 changes: 26 additions & 7 deletions biogeochem/FatesCohortMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ module FatesCohortMod
use FatesSizeAgeTypeIndicesMod, only : sizetype_class_index
use FatesSizeAgeTypeIndicesMod, only : coagetype_class_index
use FatesAllometryMod, only : carea_allom, tree_lai_sai
! [PORTED by Hui Tang: NVP allometry for cohort initialization]
use FatesAllometryMod, only : NVP_allom
use LeafBiophysicsMod, only : lb_params
use FatesInterfaceTypesMod, only : hlm_use_nvp
use PRTAllometricCarbonMod, only : ac_bc_inout_id_dbh, ac_bc_inout_id_netdc
use PRTAllometricCarbonMod, only : ac_bc_in_id_cdamage, ac_bc_in_id_pft
use PRTAllometricCarbonMod, only : ac_bc_in_id_ctrim, ac_bc_in_id_lstat
Expand Down Expand Up @@ -105,6 +109,10 @@ module FatesCohortMod
real(r8) :: c_area ! areal extent of canopy [m2]
real(r8) :: treelai ! lai of an individual within cohort leaf area [m2 leaf area/m2 crown area]
real(r8) :: treesai ! stem area index of an individual within cohort [m2 stem area/m2 crown area]
! [PORTED by Hui Tang: NVP layer thickness derived from cohort LAI via NVP_allom]
! Only meaningful for NVP cohort (stomatal_intercept(ft) <= nearzero).
! nvp_dz = treelai / (nv_sla * bulk_density_nvp); updated after each LAI change.
real(r8) :: nvp_dz ! NVP layer thickness for this cohort [m]
logical :: isnew ! flag to signify a new cohort - new cohorts have not experienced
! npp or mortality and should therefore not be fused or averaged
integer :: size_class ! index that indicates which diameter size bin the cohort currently resides in
Expand Down Expand Up @@ -375,9 +383,10 @@ subroutine NanValues(this)
this%efleaf_coh = nan
this%effnrt_coh = nan
this%efstem_coh = nan
this%c_area = nan
this%c_area = nan
this%treelai = nan
this%treesai = nan
this%nvp_dz = 0.0_r8 ! [PORTED by Hui Tang: safe default; set by NVP_allom for NVP PFTs]
this%isnew = .false.
this%size_class = fates_unset_int
this%coage_class = fates_unset_int
Expand Down Expand Up @@ -488,6 +497,7 @@ subroutine ZeroValues(this)
this%efstem_coh = 0.0_r8

this%treesai = 0._r8
this%nvp_dz = 0.0_r8 ! [PORTED by Hui Tang: safe default; set by NVP_allom for NVP PFTs]
this%size_class = 1
this%coage_class = 1

Expand Down Expand Up @@ -659,12 +669,20 @@ subroutine Create(this, prt, pft, nn, height, coage, dbh, status, &
! Query PARTEH for the leaf carbon [kg]
leaf_c = this%prt%GetState(leaf_organ, carbon12_element)

call tree_lai_sai(leaf_c, this%pft, this%c_area, this%n, &
this%canopy_layer, can_tlai, this%vcmax25top, this%dbh, this%crowndamage, &
this%canopy_trim, this%efstem_coh, 2, this%treelai, treesai)

if (hlm_use_sp .eq. ifalse) then
this%treesai = treesai
! [PORTED by Hui Tang: NVP cohorts initialize treelai and nvp_dz via NVP_allom]
if (hlm_use_nvp == itrue .and. &
lb_params%stomatal_intercept(this%pft) <= nearzero) then
call NVP_allom(this%treelai, leaf_c, this%nvp_dz, inverse=.false.)
this%treesai = 0._r8
else
call tree_lai_sai(leaf_c, this%pft, this%c_area, this%n, &
this%canopy_layer, can_tlai, this%vcmax25top, this%dbh, &
this%crowndamage, this%canopy_trim, this%efstem_coh, 2, &
this%treelai, treesai)

if (hlm_use_sp .eq. ifalse) then
this%treesai = treesai
end if
end if


Expand Down Expand Up @@ -716,6 +734,7 @@ subroutine Copy(this, copyCohort)
copyCohort%c_area = this%c_area
copyCohort%treelai = this%treelai
copyCohort%treesai = this%treesai
copyCohort%nvp_dz = this%nvp_dz ! [PORTED by Hui Tang: copy NVP layer thickness]
copyCohort%isnew = this%isnew
copyCohort%size_class = this%size_class
copyCohort%coage_class = this%coage_class
Expand Down
Loading