Skip to content

Commit 4ffab32

Browse files
authored
Merge pull request #1155 from ckoven/new_patch_insertion
New patch insertion method
2 parents f5bd625 + ec61b06 commit 4ffab32

File tree

4 files changed

+125
-167
lines changed

4 files changed

+125
-167
lines changed

biogeochem/EDPatchDynamicsMod.F90

Lines changed: 82 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -3815,156 +3815,104 @@ subroutine InsertPatch(currentSite, newPatch)
38153815

38163816
! !LOCAL VARIABLES:
38173817
type (fates_patch_type), pointer :: currentPatch
3818-
integer :: insert_method ! Temporary dev
3819-
logical :: found_landuselabel_match
3820-
integer, parameter :: unordered_lul_groups= 1
3821-
integer, parameter :: primaryland_oldest_group = 2
3822-
integer, parameter :: numerical_order_lul_groups = 3
3823-
integer, parameter :: age_order_only = 4
3824-
3825-
! Insert new patch case options:
3826-
! Option 1: Group the landuse types together, but the group order doesn't matter
3827-
! Option 2: Option 1, but primarylands are forced to be the oldest group
3828-
! Option 3: Option 1, but groups are in numerical order according to land use label index integer
3829-
! (i.e. primarylands=1, secondarylands=2, ..., croplands=5)
3830-
! Option 4: Don't group the patches by land use label. Simply add new patches to the youngest end.
3831-
3832-
! Hardcode the default insertion method. The options developed during FATES V1 land use are
3833-
! currently being held for potential future usage.
3834-
insert_method = primaryland_oldest_group
3835-
3836-
! Start from the youngest patch and work to oldest, regarless of insertion_method
3837-
currentPatch => currentSite%youngest_patch
3818+
logical :: patch_inserted
38383819

3839-
! For the three grouped cases, if the land use label of the youngest patch on the site
3840-
! is a match to the new patch land use label, simply insert it as the new youngest.
3841-
! This is applicable to the non-grouped option 4 method as well.
3842-
if (currentPatch%land_use_label .eq. newPatch%land_use_label ) then
3843-
newPatch%older => currentPatch
3844-
newPatch%younger => null()
3845-
currentPatch%younger => newPatch
3846-
currentSite%youngest_patch => newPatch
3847-
else
3848-
3849-
! If the current site youngest patch land use label doesn't match the new patch
3850-
! land use label then work through the list until you find the matching type.
3851-
! Since we've just checked the youngest patch, move to the next patch and
3852-
! initialize the match flag to false.
3853-
found_landuselabel_match = .false.
3854-
currentPatch => currentPatch%older
3855-
select case(insert_method)
3820+
! The goal here is to have patches ordered in a specific way. That way is to have them
3821+
! looped as the following, where LU refers to the land use label, PFT refers to the
3822+
! nocomp PFT label, and Y and O refer to continuous patch ages.
3823+
!
3824+
! LU1 ---- LU2 ---- LU3 -- etc
3825+
! / \ / \ / \
3826+
! PFT1 --- PFT2 | PFT1 --- PFT2 | PFT1 --- PFT2 -- etc
3827+
! / \ / \ / \ / \ / \ / \
3828+
! O - Y O - Y O - Y O - Y O - Y O - Y -- etc
38563829

3857-
! Option 1 - order of land use label groups does not matter
3858-
case (unordered_lul_groups)
3830+
! I.e. to treat land use as the outermost loop element, then nocomp PFT as next loop element,
3831+
! and then age as the innermost loop element. Visualizing the above as a linked list patches:
38593832

3860-
do while(associated(currentPatch) .and. .not. found_landuselabel_match)
3861-
if (currentPatch%land_use_label .eq. newPatch%land_use_label) then
3862-
found_landuselabel_match = .true.
3863-
else
3864-
currentPatch => currentPatch%older
3865-
end if
3866-
end do
3833+
! LU1/PFT1/O <-> LU1/PFT1/Y <-> LU1/PFT2/O <- ... -> LU3/PFT2/O <-> LU3/PFT2/Y
38673834

3868-
! In the case where we've found a land use label matching the new patch label,
3869-
! insert the newPatch will as the youngest patch for that land use type.
3870-
if (associated(currentPatch)) then
3871-
newPatch%older => currentPatch
3872-
newPatch%younger => currentPatch%younger
3873-
currentPatch%younger%older => newPatch
3874-
currentPatch%younger => newPatch
3875-
else
3876-
! In the case in which we get to the end of the list and haven't found
3877-
! a landuse label match simply add the new patch to the youngest end.
3878-
newPatch%older => currentSite%youngest_patch
3879-
newPatch%younger => null()
3880-
currentSite%youngest_patch%younger => newPatch
3881-
currentSite%youngest_patch => newPatch
3882-
endif
3883-
3884-
! Option 2 - primaryland group must be on the oldest end
3885-
case (primaryland_oldest_group)
3835+
! Mapping this setup onto the existing "older/younger" scheme means that lower number
3836+
! land use and pft labels are considered "older". Note that this matches the current
3837+
! initialization scheme in which patches are created and linked in increasing pft
3838+
! numerical order starting from 1. This also aligns with the current set_patchno scheme
3839+
! in which patches are given an indexable number for the API iteration loops.
3840+
3841+
! The way to accomplsh this most simply is to define a pseudo-age that includes all of the
3842+
! above info and sort the patches based on the pseudo-age. i.e. take some number larger
3843+
! than any patch will ever reach in actual age. Then take the LU, multiply it by the big
3844+
! number squared, add it to the pft number multiplied by the big number, and add to the age.
3845+
! And lastly to sort using that instead of the actual age.
38863846

3887-
do while(associated(currentPatch) .and. .not. found_landuselabel_match)
3888-
if (currentPatch%land_use_label .eq. newPatch%land_use_label) then
3889-
found_landuselabel_match = .true.
3890-
else
3891-
currentPatch => currentPatch%older
3892-
end if
3893-
end do
3847+
! If land use is turned off or nocomp is turned off, then this should devolve to the prior
3848+
! behavior of just age sorting.
38943849

3895-
! In the case where we've found a land use label matching the new patch label,
3896-
! insert the newPatch will as the youngest patch for that land use type.
3897-
if (associated(currentPatch)) then
3898-
newPatch%older => currentPatch
3899-
newPatch%younger => currentPatch%younger
3900-
currentPatch%younger%older => newPatch
3901-
currentPatch%younger => newPatch
3902-
else
3903-
! In the case in which we get to the end of the list and haven't found
3904-
! a landuse label match.
3905-
3906-
! If the new patch is primaryland add it to the oldest end of the list
3907-
if (newPatch%land_use_label .eq. primaryland) then
3908-
newPatch%older => null()
3909-
newPatch%younger => currentSite%oldest_patch
3910-
currentSite%oldest_patch%older => newPatch
3911-
currentSite%oldest_patch => newPatch
3912-
else
3913-
! If the new patch land use type is not primaryland and we are at the
3914-
! oldest end of the list, add it to the youngest end
3915-
newPatch%older => currentSite%youngest_patch
3916-
newPatch%younger => null()
3917-
currentSite%youngest_patch%younger => newPatch
3918-
currentSite%youngest_patch => newPatch
3919-
endif
3920-
endif
3850+
patch_inserted = .false.
3851+
3852+
if (GetPseudoPatchAge(newPatch) .le. GetPseudoPatchAge(currentSite%youngest_patch)) then
39213853

3922-
! Option 3 - groups are numerically ordered with primaryland group starting at oldest end.
3923-
case (numerical_order_lul_groups)
3854+
! insert new patch at the head of the linked list
3855+
newPatch%older => currentSite%youngest_patch
3856+
newPatch%younger => null()
3857+
currentSite%youngest_patch%younger => newPatch
3858+
currentSite%youngest_patch => newPatch
39243859

3925-
! If the youngest patch landuse label number is greater than the new
3926-
! patch land use label number, the new patch must be inserted somewhere
3927-
! in between oldest and youngest
3928-
do while(associated(currentPatch) .and. .not. found_landuselabel_match)
3929-
if (currentPatch%land_use_label .eq. newPatch%land_use_label .or. &
3930-
currentPatch%land_use_label .lt. newPatch%land_use_label) then
3931-
found_landuselabel_match = .true.
3932-
else
3933-
currentPatch => currentPatch%older
3934-
endif
3935-
end do
3860+
patch_inserted = .true.
3861+
else if (GetPseudoPatchAge(newPatch) .ge. GetPseudoPatchAge(currentSite%oldest_patch)) then
39363862

3937-
! In the case where we've found a landuse label matching the new patch label
3938-
! insert the newPatch will as the youngest patch for that land use type.
3939-
if (associated(currentPatch)) then
3863+
! insert new patch at the end of the linked list
3864+
newPatch%younger => currentSite%oldest_patch
3865+
newPatch%older => null()
3866+
currentSite%oldest_patch%older => newPatch
3867+
currentSite%oldest_patch => newPatch
39403868

3941-
newPatch%older => currentPatch
3942-
newPatch%younger => currentPatch%younger
3869+
patch_inserted = .true.
3870+
else
3871+
! new patch has a pseudo-age somewhere within the linked list. find the first patch which
3872+
! has a pseudo age older than it, and put it ahead of that patch
3873+
currentPatch => currentSite%youngest_patch
3874+
do while (associated(currentPatch) .and. ( .not. patch_inserted) )
3875+
if (GetPseudoPatchAge(newPatch) .lt. GetPseudoPatchAge(currentPatch)) then
3876+
newPatch%older => currentPatch
3877+
newPatch%younger => currentPatch%younger
39433878
currentPatch%younger%older => newPatch
3944-
currentPatch%younger => newPatch
3945-
3946-
else
3947-
3948-
! In the case were we get to the end, the new patch
3949-
! must be numerically the smallest, so put it at the oldest position
3950-
newPatch%older => null()
3951-
newPatch%younger => currentSite%oldest_patch
3952-
currentSite%oldest_patch%older => newPatch
3953-
currentSite%oldest_patch => newPatch
3879+
currentPatch%younger => newPatch
39543880

3881+
patch_inserted = .true.
39553882
endif
3883+
currentPatch => currentPatch%older
3884+
end do
3885+
end if
39563886

3957-
! Option 4 - always add the new patch as the youngest regardless of land use label
3958-
case (age_order_only)
3959-
! Set the current patch to the youngest patch
3960-
newPatch%older => currentSite%youngest_patch
3961-
newPatch%younger => null()
3962-
currentSite%youngest_patch%younger => newPatch
3963-
currentSite%youngest_patch => newPatch
3964-
end select
3887+
if ( .not. patch_inserted) then
3888+
! something has gone wrong. abort.
3889+
write(fates_log(),*) 'something has gone wrong in the patch insertion, because no place to put the new patch was found'
3890+
call endrun(msg=errMsg(sourcefile, __LINE__))
39653891
end if
39663892

3967-
end subroutine InsertPatch
3893+
end subroutine InsertPatch
3894+
3895+
! =====================================================================================
3896+
3897+
function GetPseudoPatchAge(CurrentPatch) result(pseudo_age)
3898+
3899+
! Purpose: we want to sort the patches in a way that takes into account both their
3900+
! continuous and categorical variables. Calculate a pseudo age that does this, by taking
3901+
! the integer labels, multiplying these by large numbers, and adding to the continuous age.
3902+
! Note to ensure that lower integer land use label and pft label numbers are considered
3903+
! "younger" (i.e higher index patchno) in the linked list, they are summed and multiplied by
3904+
! negative one. The patch age is still added normally to this negative pseudoage calculation
3905+
! as a higher age will result in a less negative number correlating with an "older" patch.
3906+
3907+
type (fates_patch_type), intent(in), pointer :: CurrentPatch
3908+
real(r8) :: pseudo_age
3909+
real(r8), parameter :: max_actual_age = 1.e4 ! hard to imagine a patch older than 10,000 years
3910+
real(r8), parameter :: max_actual_age_squared = 1.e8
3911+
3912+
pseudo_age = -1.0_r8 * (real(CurrentPatch%land_use_label,r8) * max_actual_age_squared + &
3913+
real(CurrentPatch%nocomp_pft_label,r8) * max_actual_age) + CurrentPatch%age
3914+
3915+
end function GetPseudoPatchAge
39683916

39693917
! =====================================================================================
39703918

main/EDInitMod.F90

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,12 @@ subroutine init_patches( nsites, sites, bc_in)
706706
do el=1,num_elements
707707
call SiteMassStock(sites(s),el,sites(s)%mass_balance(el)%old_stock, &
708708
biomass_stock,litter_stock,seed_stock)
709+
! Initialize the integrated flux balance diagnostics
710+
! No need to initialize the instantaneous states, those are re-calculated
711+
sites(s)%iflux_balance(el)%iflux_liveveg = &
712+
(biomass_stock + seed_stock)*area_inv
713+
sites(s)%iflux_balance(el)%iflux_litter = litter_stock * area_inv
714+
709715
end do
710716
call set_patchno(sites(s))
711717
enddo
@@ -972,7 +978,7 @@ subroutine init_patches( nsites, sites, bc_in)
972978
do el=1,num_elements
973979
call SiteMassStock(sites(s),el,sites(s)%mass_balance(el)%old_stock, &
974980
biomass_stock,litter_stock,seed_stock)
975-
981+
976982
! Initialize the integrated flux balance diagnostics
977983
! No need to initialize the instantaneous states, those are re-calculated
978984
sites(s)%iflux_balance(el)%iflux_liveveg = &
@@ -983,7 +989,7 @@ subroutine init_patches( nsites, sites, bc_in)
983989

984990
call set_patchno(sites(s))
985991

986-
enddo sites_loop !s
992+
enddo sites_loop
987993
end if
988994

989995
! zero all the patch fire variables for the first timestep

main/EDMainMod.F90

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,10 @@ subroutine ed_update_site( currentSite, bc_in, bc_out, is_restarting )
834834
enddo
835835

836836
! Check to see if the time integrated fluxes match the state
837-
call CheckIntegratedMassPools(currentSite)
837+
! Dont call this if we are restarting, it will double count the flux
838+
if(.not.is_restarting)then
839+
call CheckIntegratedMassPools(currentSite)
840+
end if
838841

839842
! The HLMs need to know about nutrient demand, and/or
840843
! root mass and affinities

0 commit comments

Comments
 (0)