Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track secondary forest area to keep primary and secondary patches distinct #454

Merged
merged 18 commits into from
Apr 9, 2019

Conversation

ckoven
Copy link
Contributor

@ckoven ckoven commented Dec 20, 2018

This PR creates a patch labelling system to track secondary forest patches as separate from primary forest patches. We can thus track the area, biomass, time-since-anthropogenic-disturbance, and time-since-any-disturbance separately for the secondary forest fraction.

This is based on the logging code already in the module. Secondary forest patches are generated either by anthropogenic disturbance from any patches, or by natural disturbance from other secondary forest patches. The maximum number of patches per site is changed from a scalar (default had been 10) to a vector so that we reserve a maximum number of patches in each category. Provisionally I've kept the number of primary forest patches at 10, and added a max of 4 secondary forest patches. During Patch fusion, it will only fuse patches of the same anthropogenic disturbance label.

This PR completes point 1 on the task list outlined in #450.

Collaborators:

discussions with several people based around the tasks outlined in #450.

Expectation of Answer Changes:

For the case with no harvest, this should be bit for bit with master. For cases with harvest, it will differ qualitatively.

I have done some simple tests to ensure that the code is doing what it is supposed to do. E.g.: a short run, started from inventory, with logging turned on, using a specified set of logging parameters as in the default parameter file. These generate annual logging events on the 30th day of each year, where 15% of the trees larger that 50cm are harvested during each event and 5% of all trees are killed by mechanical damage. Attached a figure showing the timeseries of secondary forest area from the simulation. The reason the growth rate is smaller than 15% per year is that the larger trees occupy only about half of the crown area to begin with and get thinned out pretty quickly by the successive logging events.

screen shot 2018-12-20 at 9 22 27 am

Checklist:

  • My change requires a change to the documentation.
  • I have updated the in-code documentation .AND. (the technical note .OR. the wiki) accordingly.
  • I have read the CONTRIBUTING document.
  • FATES PASS/FAIL regression tests were run
  • If answers were expected to change, evaluation was performed and provided

Test Results:

I have't run it through the test suite yet.

CTSM (or) E3SM (specify which) test hash-tag:

CTSM (or) E3SM (specify which) baseline hash-tag:

FATES baseline hash-tag:

Test Output:

@ckoven ckoven mentioned this pull request Dec 20, 2018
@mdietze
Copy link
Collaborator

mdietze commented Dec 20, 2018

Great to see this sort of functionality come into FATES. Two minor suggestions:

  1. Make sure to really document the semantics of this well in the external documentation and publications! Models have to be semantically precise and draw lines that are internally consistent, but those lines inevitably conflict with other peoples definitions or intuitions, and more importantly it can be different from other peoples data (e.g. LULUCF mapping) and lead to invalid benchmarks. For example, if I'm standing in the middle on a completely devegetated landscape immediately post-fire, few field ecologists would call that a primary forest. FATES does. Pretty sure ED does too. Not sure how different remote sensing products would classify it.
  2. You might consider tweaking your scheme to allow for an extensible number of classes rather than just two. In previous conversations with Moorcroft, I got the impression that the original Albani scheme had always been intended to be something that could be more extensible. Within the MANDIFORE project we're looking into leveraging that functionality to add more nuance to different management classes (e.g. production, passive, ecological, preservation, censu Becknell et al. 2015 Bioscience) that might have have different harvest frequencies, BA removal, degree of fire suppression, etc.

@jenniferholm
Copy link
Contributor

In our discussion meeting at AGU, Peter mentioned that secondary forests will never return to primary forests, and I think this is how CLM operates. I'm wondering if this should be discussed?

I'm tending to think that once a forest patch has been altered by humans maybe it can never return to primary. For example if the physical conditions of the patch, or processes of the cohorts become so altered by anthropogenic disturbance, than it cannot return.
But maybe if the harvest is a low level of selective harvesting, and the patch does not get harvested again (would that be an option?), then this would be similar to something like a natural blowdown and the surrounding late successional cohorts would aid in eventually developing a community that is very close to primary forest.

Along these same lines, do we need to distinguish when a secondary forest is different from degraded primary forest? Or is that too nuanced.

@bpbond
Copy link

bpbond commented Dec 20, 2018

@jenniferholm It seems like the whole point of that approach (once you lose primary forest status, it's gone forever) is its, um, black-or-whiteness. So I don't think you could allow any primary forest 'use' without entering a really grey area?

@ckoven 👏

@ckoven
Copy link
Contributor Author

ckoven commented Dec 20, 2018

Hi all, a few quick responses:

@mdietze Yes, I do intend to write this up in the tech note. To be clear here, what we are calling secondary forest is that way only because of logging at this point; anthropogenic fire disturbance is not yet a thing in the model. Re the point of extensibility, the way I've coded it so far should be fairly extensible if we want to add more classes. We just need to adjust the n_anthro_disturbance_categories parameter here:

integer, parameter :: n_anthro_disturbance_categories = 2
, give that category an allowable number of patches here:
integer, parameter :: maxPatchesPerSite_by_disttype(n_anthro_disturbance_categories) = (/ 10, 4 /) !!! MUST SUM TO maxPatchesPerSite !!!
and then add the logic to define what those patches would be. At this point I see the science of that as a thing to do in the second phase of this effort, where we pull in the full LUH2 transition matrix.

@jenniferholm right now the primary -> secondary forest is one way. I had thought about possibly making it allowable to go back after some long period of time, but the trick there is that within the secondary forest you still have background natural disturbance regime that causes the patches to start losing their history. if you only took the oldest patches then that would mean that only the non-gap portions of the forest got made "primary" again. So I think for now its simplest to leave it as a one-way transition.

One larger and related issue to both of your points that occurs to me is how to handle the fraction of the forest that isn't cut during a selective logging event. i.e. if we specify 15% of the forest be logged, but only of trees with some minimum diameter, then what do we do with the remaining trees? currently they stay in their original patch, which keeps the primary forest label. If we fully clear-cut some fraction of the forest (by specifying a zero minimum diameter) then it should not be an issue. But I wonder if during the logging when we do specify a minimum size, that we also take the same fraction of any canopy trees below the size threshold, and move them into a secondary forest patch to represent a degraded forest state. If so, then a question is whether we put them in the same patch or a different one.

@@ -112,7 +112,7 @@ subroutine IsItLoggingTime(is_master,currentSite)

else if(icode < 0 .and. icode > -366) then
! Logging event every year on specific day of year
if(hlm_day_of_year .eq. icode ) then
if(hlm_day_of_year .eq. abs(icode) ) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, nice catch @ckoven ! This never would had been tripped!

@rosiealice
Copy link
Contributor

@ckoven, the issue you raise about what to do with the secondary-but-unlogged forest is I think in principle similar to the issue of understory/non catastrophic fires, in that in reality there is a thinned out ecosystem that we want to retain, but it doesn't fit neatly into a 'disturbed or not-disturbed' classification (where disturbance is an all-or-nothing process). I'd be tempted to say the entire area is switched to a secondary forest labels, and then let the fusion algorithms decide what to do about it, given they can discern different densities of biomass and are allowed to fuse with any other patch, (not just their 'older' and 'younger' neighbors as per the modifications for SPITFIRE).

@ckoven
Copy link
Contributor Author

ckoven commented Jan 4, 2019

@rosiealice the more I think about it the more I agree with the solution you propose. It doesn't make sense to me to think of the trees that aren't harvested during a selective harvest event as constituting 'primary forest' anymore. So if we do that, then it would mean that our definition of secondary forest would also include degraded forest. To me that is the simplest logically consistent solution here. But perhaps we should make adding that change a separate PR since it may require more involved changes to the disturbance code.

@rosiealice
Copy link
Contributor

Yeah, I guess degraded and secondary forest are really just two points on a continuum of harvesting severity and thus can both be considered as 'not primary' and thus secondary? Maybe that's something someone that know more about these definition than me should/could weigh in on though, incase it conflicts with more widely used LUMIP/LUH2 type definitions? @dlawrenncar @lawrencepj1 @kvcalvin? @ckoven 's original query is below:

"One larger and related issue to both of your points that occurs to me is how to handle the fraction of the forest that isn't cut during a selective logging event. i.e. if we specify 15% of the forest be logged, but only of trees with some minimum diameter, then what do we do with the remaining trees? currently they stay in their original patch, which keeps the primary forest label. If we fully clear-cut some fraction of the forest (by specifying a zero minimum diameter) then it should not be an issue. But I wonder if during the logging when we do specify a minimum size, that we also take the same fraction of any canopy trees below the size threshold, and move them into a secondary forest patch to represent a degraded forest state. If so, then a question is whether we put them in the same patch or a different one."

@dlawrenncar
Copy link

dlawrenncar commented Jan 9, 2019 via email

Copy link
Contributor

@rgknox rgknox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked through restart requirements. Looks good to me, I can confirm that only the the disturbance classification and the age since anthro disturbance are necessary.

@@ -25,6 +25,10 @@ module FatesConstantsMod
! Integer equivalent of false (in case come compilers dont auto convert)
integer, parameter :: ifalse = 0

! Labels for patch disturbance history
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of named constants, two thumbs up

@@ -351,33 +358,76 @@ subroutine spawn_patches( currentSite, bc_in)
call endrun(msg=errMsg(sourcefile, __LINE__))
end if

site_areadis = site_areadis + currentPatch%area * currentPatch%disturbance_rate
! figure out whether the receiver patch for disturbance from this patch will be primary or secondary land
! receiver patch is primary forest only if both the donor patch is primary forest and the dominant disturbance type is not logging
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

! This is the amount of patch area that is disturbed, and donated by the donor
patch_site_areadis = currentPatch%area * currentPatch%disturbance_rate

! for the case where the donating patch is secondary forest, if the dominant disturbance from this patch is non-anthropogenic,
! we need to average in the time-since-anthropogenic-disturbance from the donor patch into that of the receiver patch
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ckoven, is it possible that the following logic could be true for cases where the new_patch_secondary is not allocated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, hold on this. The conflict resolution with master will require this to be irrelevant. Will submit a PR to your branch.

@ckoven
Copy link
Contributor Author

ckoven commented Feb 25, 2019

An update on this. I updated the disturbance code so that it now moves the canopy trees that aren't harvested on account of being below the minimum harvest size into the secondary patch as well. So this makes the secondary forest designation also apply to degraded forest area that has had its large trees logged, as per the discussion above.

Another subtle change that I made here is on the logging-related collateral mortality -- before that was only applied to trees above the harvest diameter with the trees below that assumed to be handled by impact mortality during harvest; but that misses the trees that were in the canopy but below the size threshold. So I've provisionally updated the logic of collateral damage during logging to apply to canopy trees irrespective of their size and let the understory trees suffer from impact mortality. But I want to check with @huangmy and @xuyi02 if this change is consistent with their vision here.

Running this through the test that I used before, with annual harvest events of 15% harvest rate of trees greater than 50cm dbh; plus an additional 5% of collateral mortality and 5% of logging infrastructure mortality; this should therefore give 25% of the forest area as secondary after the first harvest. (Or, more precisely, 25% of the original area that is under tree crowns, which in this case is very slightly less). It now does so:

secondary_forest_fraction

I think this should be ready for testing and review now.

@huangmy
Copy link

huangmy commented Feb 25, 2019

@ckoven : I went through the comments and the code, and your modification make sense to me logically. On the other hand, I tried to recall why I made that decision a while ago after talking to @rgknox . I am sure that I did that in case there were too may small trees/biomass in the canopy layer, which would lead to a large flux to CWD and therefore downstream processes. My numerical experiments suggested that it was a possibility at least at that moment. Is that less a problem after your recent changes?

@ckoven
Copy link
Contributor Author

ckoven commented Feb 26, 2019

@huangmy Great, thanks for looking over the changes. I see your point about the possibility that the model could generate too much CWD, and its possible that high CWD could still be a problem. But if it is, then to me a better solution would be to reduce the value of the collateral damage parameter and keep the logic most self-consistent, rather than to keep the nominal value of the collateral damage parameter high, but apply the size-class filter; especially given that the collateral damage parameter strikes me as pretty unconstrained by the literature. Would you be ok with that solution if it were needed?

@huangmy
Copy link

huangmy commented Feb 26, 2019

@ckoven Yes, you are correct that the collateral damage parameter is not well-constrained. So this solution sounds good to me. Please feel free to proceed. We might need to add a note to the parameter file or the user guide on this so that users are aware of potential issues.

@ckoven ckoven changed the title Track secondary forest area keep primary and secondary patches distinct Track secondary forest area to keep primary and secondary patches distinct Mar 4, 2019
@@ -286,6 +287,11 @@ subroutine ed_integrate_state_variables(currentSite, bc_in )
currentPatch%patchno,currentPatch%area
endif

! add age increment to secondary forest patches as well
if (currentPatch%anthro_disturbance_label .eq. secondaryforest) then
currentPatch%age_since_anthro_disturbance = currentPatch%age_since_anthro_disturbance + hlm_freq_day
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ckoven , does age_since_anthro_disturbance tell us anything different than "age"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. age gets reset whenever any disturbance happens, age_since_anthro_disturbance only gets reset during a harvest event. So, if you have a secondary forest patch that also has other disturbance on it, this information allows you to do something like still go and harvest all the land on a set forest rotation schedule.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if a secondary forest patch experiences a tree-fall event, the patch that is created should not reset its age_since_antrho_disturbance, instead it retains that value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the idea.

!INSERT SURVIVORS FROM DISTURBANCE INTO NEW PATCH
currentCohort => currentPatch%shortest
do while(associated(currentCohort))
if (patch_site_areadis > nearzero ) then

allocate(nc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole allocation block needs to be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, good catch.


! next create patch to receive secondary forest area
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked through to see that age is set to zero for the new secondary patch, and that age sets the age since anthro disturbance, and that angtho age is updated when secondary patches donate area through natural means. If they donate through a logging event, then they contribute a zero to the weighted age of the new secondary patch. Makes sense to me.

@rgknox
Copy link
Contributor

rgknox commented Mar 27, 2019

testing

@rgknox
Copy link
Contributor

rgknox commented Mar 28, 2019

All expected pass b4b with master, except the 2 year 1x1 brazil, which did not complete due to exceeding the time limit (2 hours). The baseline required 30 minutes. I suspect that the node just got hung up. Will check through the differences in time on the other tests.

@rgknox
Copy link
Contributor

rgknox commented Mar 28, 2019

SMS_Lm6.f45_f45_mg37 went from 55.792 -> 70.595 seconds
ERS_Ld60.f45_f45_mg37 (run 1) 21.915 seconds -> 19.248
ERS_Ld60.f45_f45_mg37 (run 1) 19.358 seconds -> 22.352 seconds

I will re-test

@rgknox
Copy link
Contributor

rgknox commented Apr 4, 2019

I re-tested and found that the 2 year test completed in about 11-12 minutes, well within the expected time. I can only guess that because this test takes longer, it experienced machine related slow-downs that only it experienced.

/glade/scratch/rgknox/clmed-tests/fates.cheyenne.intel.secondary-0sec-extendwall-v2-Ce862304-F033090c/

I will retest one more time though to be sure, then will integrate.

Also performed 50 year tests on the f10 grid, simulation completed:

/gpfs/fs1/scratch/rgknox/fates-clm-tests/12pft-demprom-secondary-loopcheck-f10_f10_mg37

@rgknox
Copy link
Contributor

rgknox commented Apr 8, 2019

sent a conflict resolution to @ckoven. All pass on that branch:

/gpfs/fs1/scratch/rgknox/clmed-tests/secondary-final0-Cbc17668-F30f0809.fates.cheyenne.intel

will integrate pending acceptance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants