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/.gitmodules b/.gitmodules index 7dc4d3c410..7fa78ed7ad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,9 @@ 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-functions"] + path = tools/xarray-functions + url = https://github.com/NGEET/tools-fates-xarray.git + fxtag = v0.0.0 + fxrequired = AlwaysRequired + fxDONOTUSEurl = https://github.com/NGEET/tools-fates-xarray 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 e12fcd81ee..6b44fccd2c 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 @@ -73,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 @@ -87,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 @@ -119,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) @@ -135,7 +155,6 @@ subroutine canopy_structure( currentSite , bc_in ) ! ! !USES: - use EDParamsMod, only : ED_val_comp_excln use EDTypesMod , only : min_patch_area ! @@ -150,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 @@ -178,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 ! ------------------------------------------------------------------------------ @@ -185,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 @@ -206,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 ! --------------------------------------------------------------------------------------- @@ -278,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' @@ -328,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)) @@ -349,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) + ! -------------------------------------------------------------- + ! 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 + ! -------------------------------------------------------------- - use EDParamsMod, only : ED_val_comp_excln - - ! !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: @@ -1407,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 @@ -2082,7 +1552,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 @@ -2269,21 +1739,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 @@ -2297,31 +1761,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 1acc2a7835..ee1e2d4a7c 100644 --- a/biogeochem/EDCohortDynamicsMod.F90 +++ b/biogeochem/EDCohortDynamicsMod.F90 @@ -340,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. & ! @@ -354,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. & @@ -387,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) @@ -817,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 @@ -941,7 +944,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 @@ -1020,6 +1023,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 @@ -1032,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 + & @@ -1118,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() diff --git a/biogeochem/EDLoggingMortalityMod.F90 b/biogeochem/EDLoggingMortalityMod.F90 index 3781a93077..3f8dd0c756 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 @@ -289,6 +294,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. @@ -1146,9 +1152,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/EDPatchDynamicsMod.F90 b/biogeochem/EDPatchDynamicsMod.F90 index 9622669409..204d17dc72 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 @@ -433,7 +432,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. @@ -483,7 +482,7 @@ end subroutine disturbance_rates ! ============================================================================ - subroutine spawn_patches( currentSite, bc_in, bc_out) + subroutine spawn_patches( currentSite, bc_in ) ! ! !DESCRIPTION: ! In this subroutine, the following happens, @@ -510,7 +509,6 @@ 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(inout) :: bc_out ! ! !LOCAL VARIABLES: type (fates_patch_type) , pointer :: newPatch @@ -569,8 +567,6 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! 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) @@ -757,7 +753,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) 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 @@ -773,13 +769,13 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) 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 @@ -977,23 +973,61 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ! 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) = & @@ -1002,6 +1036,19 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) ( (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) = & @@ -1011,6 +1058,20 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) 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) @@ -1044,12 +1105,18 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) 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 @@ -1073,11 +1140,16 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) currentSite%mass_balance(el)%burn_flux_to_atm + & leaf_burn_frac * leaf_m * nc%n - 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 + ! 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) @@ -1423,7 +1495,7 @@ subroutine spawn_patches( currentSite, bc_in, bc_out) 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 @@ -1526,7 +1598,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, 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 @@ -1635,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 @@ -1646,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 @@ -1683,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 @@ -1818,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) ! ----------------------------------------------------------------------------------- ! @@ -1867,9 +1938,8 @@ 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 type(site_massbal_type), pointer :: site_mass type(litter_type),pointer :: curr_litt ! litter object for current patch @@ -1898,9 +1968,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) + & @@ -1993,10 +2067,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 * ha_per_m2 * days_per_sec - ! 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 @@ -2024,8 +2095,6 @@ 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 - ! Transfer root fines (none burns) do sl = 1,currentSite%nlevsoil donatable_mass = curr_litt%root_fines(dcmpy,sl) * patch_site_areadis @@ -2075,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. @@ -2095,8 +2164,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(inout) :: bc_out - + ! ! !LOCAL VARIABLES: @@ -2217,8 +2285,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) * & @@ -2237,8 +2305,6 @@ 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 - call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2300,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 @@ -2310,7 +2375,7 @@ subroutine fire_litter_fluxes(currentSite, currentPatch, & currentCohort => currentCohort%taller enddo - end do + end do return end subroutine fire_litter_fluxes @@ -2391,7 +2456,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) @@ -2551,7 +2616,7 @@ end subroutine mortality_litter_fluxes ! ============================================================================ subroutine landusechange_litter_fluxes(currentSite, currentPatch, & - newPatch, patch_site_areadis, bc_in, bc_out, & + newPatch, patch_site_areadis, bc_in, & clearing_matrix_element) ! ! !DESCRIPTION: @@ -2568,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 ! @@ -2606,8 +2670,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] !--------------------------------------------------------------------- @@ -2650,9 +2712,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) @@ -2713,8 +2772,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 - call set_root_fraction(currentSite%rootfrac_scr, pft, currentSite%zi_soil, & bc_in%max_rooting_depth_index_col) @@ -2774,7 +2831,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)) @@ -2788,10 +2845,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 - 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 + & @@ -2809,15 +2863,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 @@ -3219,19 +3264,19 @@ 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 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 @@ -3593,12 +3638,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 a944ce4eba..49e671da2a 100644 --- a/biogeochem/EDPhysiologyMod.F90 +++ b/biogeochem/EDPhysiologyMod.F90 @@ -20,8 +20,6 @@ module EDPhysiologyMod use FatesInterfaceTypesMod, only : hlm_use_nocomp 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 use FatesInterfaceTypesMod, only : hlm_use_ed_prescribed_phys use FatesConstantsMod, only : r8 => fates_r8 @@ -149,7 +147,8 @@ module EDPhysiologyMod use PRTInitParamsFatesMod, only : NewRecruitTotalStoichiometry use FatesInterfaceTypesMod, only : hlm_use_luh use FatesInterfaceTypesMod, only : hlm_regeneration_model - + + implicit none private @@ -160,7 +159,6 @@ module EDPhysiologyMod public :: calculate_SP_properties public :: recruitment public :: ZeroLitterFluxes - public :: ZeroAllocationRates public :: PreDisturbanceLitterFluxes public :: PreDisturbanceIntegrateLitter @@ -169,14 +167,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 @@ -201,7 +199,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. ! ============================================================================ @@ -260,15 +258,11 @@ subroutine ZeroAllocationRates( currentSite ) return 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 @@ -288,10 +282,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 @@ -303,12 +297,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 @@ -320,7 +314,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() @@ -328,37 +322,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 @@ -369,7 +363,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) + & @@ -380,7 +374,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 @@ -388,12 +382,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 @@ -403,14 +397,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 @@ -420,7 +414,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) ndcohort%taller => null() endif ccohort%taller => ndcohort - + end if if_numtrees end do do_dclass @@ -428,7 +422,7 @@ subroutine GenerateDamageAndLitterFluxes( csite, cpatch, bc_in ) end associate ccohort => ccohort%shorter enddo - + return end subroutine GenerateDamageAndLitterFluxes @@ -475,33 +469,34 @@ subroutine PreDisturbanceLitterFluxes( currentSite, currentPatch, bc_in, bc_out site_mass => currentSite%mass_balance(el), & diag => currentSite%flux_diags%elem(el)) - ! 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, bc_out) + ! Calculate loss rate of viable seeds to litter + 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), currentPatch) - ! Only calculate fragmentation flux over layers that are active - ! (RGK-Mar2019) SHOULD WE MAX THIS AT 1? DONT HAVE TO + ! 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) - nlev_eff_decomp = max(bc_in%max_rooting_depth_index_col, 1) - call CWDOut(litt,currentPatch%fragmentation_scaler,nlev_eff_decomp) + ! 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)) + ! 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 end associate end do @@ -669,7 +664,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 @@ -730,7 +725,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) @@ -762,7 +757,7 @@ subroutine trim_canopy( currentSite ) kn = DecayCoeffVcmax(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) @@ -953,8 +948,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 @@ -965,14 +960,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. @@ -1172,7 +1167,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. @@ -1189,7 +1184,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 @@ -1211,9 +1206,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 @@ -1232,13 +1227,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 @@ -1285,7 +1280,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,:)))) @@ -1308,10 +1303,10 @@ subroutine phenology( currentSite, bc_in ) 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 + ! 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 @@ -1349,7 +1344,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). !---~--- @@ -1394,7 +1389,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 @@ -1595,7 +1590,6 @@ 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 integer :: ipft ! Plant functional type index real(r8), parameter :: leaf_drop_fraction = 1.0_r8 @@ -1622,7 +1616,6 @@ 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) ! 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 @@ -1656,15 +1649,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 @@ -1675,7 +1668,7 @@ subroutine phenology_leafonoff(currentSite) call bleaf(currentCohort%dbh,currentCohort%pft,currentCohort%crowndamage, & currentCohort%canopy_trim,currentCohort%efleaf_coh,target_leaf_c) call bfineroot(currentCohort%dbh,currentCohort%pft, & - currentCohort%canopy_trim,l2fr,currentCohort%effnrt_coh,target_fnrt_c) + currentCohort%canopy_trim,currentCohort%l2fr,currentCohort%effnrt_coh,target_fnrt_c) call bsap_allom(currentCohort%dbh,currentCohort%pft,currentCohort%crowndamage, & currentCohort%canopy_trim,currentCohort%efstem_coh,sapw_area,target_sapw_c) call bagw_allom(currentCohort%dbh,currentCohort%pft,currentCohort%crowndamage,& @@ -1717,7 +1710,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, & @@ -1747,7 +1740,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 ) ) ) @@ -1953,13 +1946,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 @@ -1999,7 +1992,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] @@ -2047,7 +2040,7 @@ subroutine assign_cohort_SP_properties(currentCohort, htop, tlai, tsai, parea, i end subroutine assign_cohort_SP_properties ! ===================================================================================== - + subroutine SeedUpdate( currentSite ) ! ----------------------------------------------------------------------------------- @@ -2064,7 +2057,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 ! @@ -2088,6 +2080,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 @@ -2101,6 +2098,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)) @@ -2124,7 +2134,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) @@ -2142,13 +2152,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 @@ -2160,25 +2163,41 @@ 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. & 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 @@ -2191,15 +2210,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] + 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] 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 @@ -2211,7 +2230,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 @@ -2219,33 +2238,32 @@ end subroutine SeedUpdate ! ============================================================================ - subroutine SeedDecay( litt , currentPatch, bc_in ) + subroutine SeedDecay( litt , currentPatch ) ! ! !DESCRIPTION: ! 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 ! ! !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 @@ -2253,9 +2271,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 @@ -2267,45 +2285,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 @@ -2315,31 +2333,31 @@ 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 ! ============================================================================ - 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 + ! Flux from seed bank into the seedling pool ! ! !USES: @@ -2348,12 +2366,11 @@ 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: 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) @@ -2381,31 +2398,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)) @@ -2415,11 +2432,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 @@ -2428,7 +2445,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 @@ -2480,11 +2497,11 @@ 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) :: l2fr ! leaf to fineroot biomass ratio [0-1] + real(r8) :: cohort_n ! new cohort density + real(r8) :: l2fr ! leaf to fineroot biomass ratio [-] real(r8) :: c_leaf ! target leaf biomass [kgC] real(r8) :: c_fnrt ! target fine root biomass [kgC] real(r8) :: c_sapw ! target sapwood biomass [kgC] @@ -2501,14 +2518,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 @@ -2548,7 +2565,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 @@ -2583,8 +2600,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 @@ -2656,11 +2673,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) @@ -2669,7 +2686,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 @@ -2726,7 +2743,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) @@ -2769,7 +2786,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 @@ -2795,7 +2812,7 @@ end subroutine recruitment ! ====================================================================================== - subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) + subroutine CWDInput( currentSite, currentPatch, litt, bc_in) ! ! !DESCRIPTION: @@ -2815,7 +2832,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: @@ -2884,7 +2900,7 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) site_mass => currentSite%mass_balance(element_pos(element_id)) ! Transfer litter from turnover of living plants - + currentCohort => currentPatch%shortest do while(associated(currentCohort)) @@ -2895,9 +2911,9 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) 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) @@ -2969,9 +2985,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 @@ -3142,10 +3155,6 @@ subroutine CWDInput( currentSite, currentPatch, litt, bc_in, bc_out) (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 + & @@ -3309,16 +3318,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 @@ -3332,11 +3341,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 @@ -3344,7 +3353,7 @@ subroutine UpdateRecruitL2FR(csite) do while(associated(cpatch)) rec_count(1:numpft,1:nclmax) = 0 - + ccohort => cpatch%shortest cloop: do while(associated(ccohort)) @@ -3393,16 +3402,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) = & @@ -3418,15 +3427,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) @@ -3434,9 +3443,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 @@ -3453,7 +3462,7 @@ subroutine SetRecruitL2FR(csite) cpatch => cpatch%older end do - + return end subroutine SetRecruitL2FR diff --git a/biogeochem/FatesCohortMod.F90 b/biogeochem/FatesCohortMod.F90 index 399f4b894d..84ad0f2247 100644 --- a/biogeochem/FatesCohortMod.F90 +++ b/biogeochem/FatesCohortMod.F90 @@ -273,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 !--------------------------------------------------------------------------- @@ -452,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 !=========================================================================== @@ -538,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 @@ -783,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 @@ -1121,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 dd32bbfc80..bdf572039a 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 @@ -130,8 +151,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) !--------------------------------------------------------------------------- @@ -153,11 +174,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] @@ -211,9 +227,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] @@ -271,7 +297,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() @@ -342,8 +369,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 @@ -371,14 +396,12 @@ 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)) 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 @@ -395,8 +418,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 @@ -407,8 +430,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 @@ -505,6 +526,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 @@ -523,8 +550,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 @@ -537,8 +564,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 @@ -564,13 +589,13 @@ 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 this%fabd(:) = 0.0_r8 this%sabs_dir(:) = 0.0_r8 this%sabs_dif(:) = 0.0_r8 + this%rad_error(:) = hlm_hio_ignore_val ! ROOTS this%btran_ft(:) = 0.0_r8 @@ -595,6 +620,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 @@ -880,6 +909,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 @@ -903,8 +933,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 @@ -1143,7 +1171,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 @@ -1151,11 +1179,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 @@ -1168,6 +1206,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/biogeochem/FatesSoilBGCFluxMod.F90 b/biogeochem/FatesSoilBGCFluxMod.F90 index e0af2a9e0f..8b6aad7542 100644 --- a/biogeochem/FatesSoilBGCFluxMod.F90 +++ b/biogeochem/FatesSoilBGCFluxMod.F90 @@ -54,6 +54,8 @@ module FatesSoilBGCFluxMod use FatesInterfaceTypesMod, only : hlm_parteh_mode use FatesInterfaceTypesMod, only : hlm_use_ch4 use FatesInterfaceTypesMod, only : hlm_decomp + use FatesInterfaceTypesMod, only : hlm_phosphorus_suppl + use FatesInterfaceTypesMod, only : hlm_nitrogen_suppl use FatesConstantsMod , only : prescribed_p_uptake use FatesConstantsMod , only : prescribed_n_uptake use FatesConstantsMod , only : coupled_p_uptake @@ -68,7 +70,7 @@ module FatesSoilBGCFluxMod use FatesConstantsMod, only : days_per_year use FatesConstantsMod, only : sec_per_day use FatesConstantsMod, only : years_per_day - use FatesConstantsMod, only : itrue + use FatesConstantsMod, only : itrue,ifalse use FatesConstantsMod, only : nocomp_bareground use FatesLitterMod, only : litter_type use FatesLitterMod , only : ncwd @@ -100,7 +102,7 @@ module FatesSoilBGCFluxMod ! ===================================================================================== - subroutine UnPackNutrientAquisitionBCs(sites, bc_in) + subroutine UnPackNutrientAquisitionBCs(sites, bc_in, nitr_suppl, phos_suppl) ! ----------------------------------------------------------------------------------- ! The purpose of this routine is to recieve the nutrient uptake flux @@ -118,8 +120,10 @@ subroutine UnPackNutrientAquisitionBCs(sites, bc_in) ! !ARGUMENTS type(ed_site_type), intent(inout) :: sites(:) - type(bc_in_type), intent(in) :: bc_in(:) - + type(bc_in_type), intent(in) :: bc_in(:) + logical, intent(in) :: nitr_suppl ! Is the HLM supplementing nitrogen? + logical, intent(in) :: phos_suppl ! Is the HLM supplementing phosphorus? + ! Locals integer :: nsites ! number of sites integer :: s ! site loop index @@ -129,6 +133,22 @@ subroutine UnPackNutrientAquisitionBCs(sites, bc_in) type(fates_cohort_type), pointer :: ccohort ! current cohort pointer real(r8) :: fnrt_c ! fine-root carbon [kg] + ! FATES needs to know the supplementation status of N and P in the soils + ! If both are supplemented, then FATES doesn't activate dynamic roots + ! even if we are in CNP mode. + + if(nitr_suppl) then + hlm_nitrogen_suppl = itrue + else + hlm_nitrogen_suppl = ifalse + end if + + if(phos_suppl) then + hlm_phosphorus_suppl = itrue + else + hlm_phosphorus_suppl = ifalse + end if + nsites = size(sites,dim=1) ! We can exit if this is a c-only simulation 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/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/SFFireWeatherMod.F90 b/fire/SFFireWeatherMod.F90 index 3191b460b1..8609912e37 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/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 + 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 dc127a9588..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 @@ -60,6 +62,7 @@ subroutine DailyFireModel(currentSite, bc_in) call CalculateSurfaceRateOfSpread(currentSite) call CalculateSurfaceFireIntensity(currentSite) call CalculateAreaBurnt(currentSite) + call CalculateRxFireAreaBurnt(currentSite) call CalculatePostFireMortality(currentSite) end if @@ -70,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 @@ -120,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 !--------------------------------------------------------------------------------------- @@ -227,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 @@ -296,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 @@ -341,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 @@ -351,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)) @@ -367,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 @@ -399,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 @@ -421,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) @@ -441,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 @@ -451,7 +504,67 @@ subroutine CalculateAreaBurnt(currentSite) end subroutine CalculateAreaBurnt !--------------------------------------------------------------------------------------- + + 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 + 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 + + ! 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 /= 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 + + end subroutine CalculateRxFireAreaBurnt + !--------------------------------------------------------------------------------------- + subroutine CalculatePostFireMortality(currentSite) ! ! DESCRIPTION: diff --git a/fire/SFNesterovMod.F90 b/fire/SFNesterovMod.F90 index 23128c880f..3a75a8b2e2 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 @@ -48,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/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/main/ChecksBalancesMod.F90 b/main/ChecksBalancesMod.F90 index 325a1089e8..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 = 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) diff --git a/main/EDInitMod.F90 b/main/EDInitMod.F90 index 13323e72eb..60f6892d70 100644 --- a/main/EDInitMod.F90 +++ b/main/EDInitMod.F90 @@ -152,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)) @@ -166,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)) @@ -178,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)) @@ -185,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)) @@ -318,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 @@ -329,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 @@ -355,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 @@ -1025,6 +1081,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 diff --git a/main/EDMainMod.F90 b/main/EDMainMod.F90 index afb3c64a54..d7797c3599 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) @@ -294,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) @@ -320,6 +328,7 @@ subroutine ed_ecosystem_dynamics(currentSite, bc_in, bc_out) ! Final instantaneous mass balance check call TotalBalanceCheck(currentSite,5) + end subroutine ed_ecosystem_dynamics !-------------------------------------------------------------------------------! @@ -417,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) @@ -636,21 +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) * & - AREA_INV * currentCohort%n / hlm_days_per_year / 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 ) @@ -672,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 @@ -685,7 +676,7 @@ 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 +705,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 +773,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, bc_out) @@ -844,28 +835,35 @@ 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 !----------------------------------------------------------------------- + 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 + + ! 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) + call canopy_spread(currentSite) + else + site_cmass%gpp_acc = 0._r8 + 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) - call SiteMassStock(currentSite,1,total_stock,biomass_stock,litter_stock,seed_stock) - bc_out%fates_total_carbon_site = total_stock - ! Update recruit L2FRs based on new canopy position call SetRecruitL2FR(currentSite) @@ -913,21 +911,44 @@ 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 * area_inv * days_per_sec + bc_out%grazing_closs_to_atm_si = site_cmass%herbivory_flux_out * area_inv * days_per_sec + 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,optional , intent(in) :: is_restarting ! is the model going through its restart init procedure? + ! ! !LOCAL VARIABLES: type(site_massbal_type),pointer :: site_mass @@ -947,7 +968,7 @@ subroutine TotalBalanceCheck (currentSite, call_index ) real(r8) :: store_m ! "" storage real(r8) :: struct_m ! "" structure real(r8) :: repro_m ! "" reproduction - + 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 @@ -962,38 +983,46 @@ subroutine TotalBalanceCheck (currentSite, call_index ) logical, parameter :: print_cohorts = .true. ! Set to true if you want ! to print cohort data ! upon fail (lots of text) + l_is_restarting = .false. + if(present(is_restarting))then + l_is_restarting = is_restarting + end if + !----------------------------------------------------------------------- if(hlm_use_sp.eq.ifalse)then change_in_stock = 0.0_r8 - ! 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) call SiteMassStock(currentSite,el,total_stock,biomass_stock,litter_stock,seed_stock) change_in_stock = total_stock - site_mass%old_stock + if(l_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 - 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 - net_flux = flux_in - flux_out error = abs(net_flux - change_in_stock) @@ -1091,12 +1120,13 @@ subroutine TotalBalanceCheck (currentSite, call_index ) ! 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. l_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 @@ -1161,14 +1191,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 b90b547dad..1518f75be5 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 @@ -65,6 +65,7 @@ module EDParamsMod 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 + 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) @@ -80,10 +81,10 @@ 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) - ! 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 + 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 @@ -302,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 @@ -609,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) @@ -824,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 6fa91f6bcb..851bf47fcf 100644 --- a/main/EDPftvarcon.F90 +++ b/main/EDPftvarcon.F90 @@ -23,7 +23,6 @@ module EDPftvarcon use PRTGenericMod, only : leaf_organ, fnrt_organ, store_organ use PRTGenericMod, only : sapw_organ, struct_organ, repro_organ use PRTGenericMod, only : prt_cnp_flex_allom_hyp,prt_carbon_allom_hyp - 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 @@ -1768,28 +1767,21 @@ subroutine FatesCheckParams(is_master) write(fates_log(),*) 'Aborting' call endrun(msg=errMsg(sourcefile, __LINE__)) end if - ! If nitrogen is turned on, check to make sure there are valid ammonium ! parameters - if(hlm_nitrogen_spec>0)then - if (trim(hlm_nu_com).eq.'ECA') then - - if(any(EDpftvarcon_inst%eca_km_nh4(:)<0._r8) ) then - write(fates_log(),*) 'ECA with nitrogen is turned on' - write(fates_log(),*) 'bad ECA km value(s) for nh4: ',EDpftvarcon_inst%eca_km_nh4(:) - write(fates_log(),*) 'Aborting' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - if(hlm_nitrogen_spec==2)then - if(any(EDpftvarcon_inst%eca_km_no3(:)<0._r8)) then - write(fates_log(),*) 'ECA with nit/denitr is turned on' - write(fates_log(),*) 'bad ECA km value(s) for no3: ',EDpftvarcon_inst%eca_km_no3(:) - write(fates_log(),*) 'Aborting' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - end if + if (trim(hlm_nu_com).eq.'ECA') then + if(any(EDpftvarcon_inst%eca_km_nh4(:)<0._r8) ) then + write(fates_log(),*) 'ECA with nitrogen is turned on' + write(fates_log(),*) 'bad ECA km value(s) for nh4: ',EDpftvarcon_inst%eca_km_nh4(:) + write(fates_log(),*) 'Aborting' + call endrun(msg=errMsg(sourcefile, __LINE__)) + end if + if(any(EDpftvarcon_inst%eca_km_no3(:)<0._r8)) then + write(fates_log(),*) 'ECA with nit/denitr is turned on' + write(fates_log(),*) 'bad ECA km value(s) for no3: ',EDpftvarcon_inst%eca_km_no3(:) + write(fates_log(),*) 'Aborting' + call endrun(msg=errMsg(sourcefile, __LINE__)) end if end if @@ -1819,8 +1811,8 @@ subroutine FatesCheckParams(is_master) ! We are using a simple phosphatase model right now. There is ! no critical value (lambda) , and there is no preferential uptake (alpha). ! Make sure these parameters are both set to 0. - - if ((hlm_phosphorus_spec>0) .and. (trim(hlm_nu_com).eq.'ECA')) then + + if (trim(hlm_nu_com).eq.'ECA') then if (any(abs(EDPftvarcon_inst%eca_lambda_ptase(:)) > nearzero ) ) then write(fates_log(),*) 'Critical Values for phosphatase in ECA are not' write(fates_log(),*) 'enabled right now. Please set fates_eca_lambda_ptase = 0' diff --git a/main/EDTypesMod.F90 b/main/EDTypesMod.F90 index 3a2a4b7386..72a8614992 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 @@ -219,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 @@ -456,6 +451,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 +497,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 +516,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 +546,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 +563,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 @@ -644,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 @@ -660,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/FatesHistoryInterfaceMod.F90 b/main/FatesHistoryInterfaceMod.F90 index 76b941e7c1..f5070a2af3 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 @@ -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 @@ -430,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 @@ -450,16 +449,26 @@ module FatesHistoryInterfaceMod integer :: ih_fire_nignitions_si integer :: ih_fire_fdi_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_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 @@ -504,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 @@ -561,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 @@ -667,14 +680,21 @@ module FatesHistoryInterfaceMod 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 @@ -2347,10 +2367,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 @@ -2362,7 +2382,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 == @@ -2376,7 +2396,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 @@ -2426,6 +2445,7 @@ subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) 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, & @@ -2439,6 +2459,15 @@ subroutine update_history_dyn_sitelevel(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_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, & @@ -2474,7 +2503,6 @@ subroutine update_history_dyn_sitelevel(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, & @@ -2548,10 +2576,6 @@ subroutine update_history_dyn_sitelevel(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) @@ -2559,6 +2583,9 @@ subroutine update_history_dyn_sitelevel(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 @@ -2566,6 +2593,15 @@ subroutine update_history_dyn_sitelevel(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 @@ -2660,7 +2696,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 @@ -2695,12 +2731,22 @@ subroutine update_history_dyn_sitelevel(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_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_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 @@ -2857,11 +2903,15 @@ subroutine update_history_dyn_sitelevel(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 @@ -2921,16 +2971,16 @@ subroutine update_history_dyn_sitelevel(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%SumMortForHistory(per_year = .false.) * total_m * ccohort%n * ha_per_m2 @@ -2939,7 +2989,6 @@ subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) 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%SumMortForHistory(per_year = .false.) * total_m * ccohort%n * ha_per_m2 @@ -2948,9 +2997,15 @@ subroutine update_history_dyn_sitelevel(this,nc,nsites,sites,bc_in) 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 @@ -3173,9 +3228,12 @@ subroutine update_history_dyn_subsite(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, & @@ -3189,7 +3247,8 @@ subroutine update_history_dyn_subsite(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, & @@ -4282,15 +4341,24 @@ subroutine update_history_dyn_subsite(this,nc,nsites,sites,bc_in) 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) + & @@ -4318,7 +4386,7 @@ subroutine update_history_dyn_subsite(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 @@ -4355,18 +4423,18 @@ subroutine update_history_dyn_subsite(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 @@ -4399,7 +4467,8 @@ subroutine update_history_dyn_subsite(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) + & @@ -4692,7 +4761,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 == @@ -4706,7 +4775,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 @@ -4732,6 +4800,8 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) 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, & @@ -4742,7 +4812,9 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) 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_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, & @@ -4785,6 +4857,15 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) 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 @@ -4794,7 +4875,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 @@ -4836,6 +4917,14 @@ subroutine update_history_dyn_subsite_ageclass(this,nc,nsites,sites,bc_in) ! 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)) @@ -4981,6 +5070,22 @@ subroutine reset_history_dyn_subsite(this, nsites, sites) 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 @@ -4996,6 +5101,16 @@ subroutine reset_history_dyn_subsite(this, nsites, sites) 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 @@ -5020,9 +5135,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_in,bc_out,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 @@ -5031,7 +5146,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 == @@ -5046,7 +5161,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 @@ -5109,6 +5223,11 @@ subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_t ! 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)) @@ -5117,7 +5236,7 @@ subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_t ! 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 @@ -5126,23 +5245,22 @@ subroutine update_history_hifrq_sitelevel(this,nc,nsites,sites,bc_in,bc_out,dt_t 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 + 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 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 @@ -5254,7 +5372,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_in,dt_tstep) ! --------------------------------------------------------------------------------- ! This subroutine is intended to update all history variables with upfreq == @@ -5269,9 +5387,8 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst 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) - type(bc_out_type) , intent(in) :: bc_out(nsites) real(r8) , intent(in) :: dt_tstep ! Locals @@ -5288,7 +5405,8 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst 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) @@ -5358,8 +5476,10 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst 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)) @@ -5440,8 +5560,8 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst 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 @@ -5453,186 +5573,194 @@ subroutine update_history_hifrq_subsite(this,nc,nsites,sites,bc_in,bc_out,dt_tst 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) - - ! calculate where we are on multiplexed dimensions - clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax - cnlf_indx = ileaf + (ican-1) * nlevleaf + do_pft1: do ipft=1,numpft + do_canlev1: do ican=1,cpatch%ncl_p + do_leaflev1: do ileaf=1,cpatch%nrad(ican,ipft) - ! 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 + ! calculate where we are on multiplexed dimensions + clllpf_indx = ileaf + (ican-1) * nlevleaf + (ipft-1) * nlevleaf * nclmax + cnlf_indx = ileaf + (ican-1) * nlevleaf - clllpf_area = cpatch%canopy_area_profile(ican,ipft,ileaf)*cpatch%total_canopy_area + ! 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 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 + clllpf_area = cpatch%canopy_area_profile(ican,ipft,ileaf)*cpatch%total_canopy_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 + ! 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 - ! elai_profile is the m2 of leaf inside the m2 of bin. + 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_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 + ! elai_profile is the m2 of leaf inside the m2 of bin. - 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_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_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 + 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_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_dir_z = bc_in(s)%solad_parb(cpatch%patchno,ipar) * & + cpatch%nrmlzd_parprof_pft_dir_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 + 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) + & + parprof_pft_dir_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 + hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) = hio_parprof_dif_si_cnlfpft(io_si,clllpf_indx) + & + parprof_pft_dif_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 + ! 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_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_parprof_dif_si_cnlf(io_si,cnlf_indx) = hio_parprof_dif_si_cnlf(io_si,cnlf_indx) + & + parprof_pft_dif_z * 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_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_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_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_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_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 - ! Canopy mean diagnostics - ! -------------------------------------------------------------- + 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_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 + ! Canopy mean diagnostics + ! -------------------------------------------------------------- - 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_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 - 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 + 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 ! ---------------------------------------------------------------------------- - - 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)1) then call this%set_history_var(vname='FATES_NPP_LU', units='kg m-2 s-1', & @@ -6977,7 +7180,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', & @@ -7149,18 +7352,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 @@ -7197,7 +7394,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' @@ -7209,12 +7406,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, & @@ -7244,14 +7435,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', & @@ -7265,7 +7456,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', & @@ -7300,18 +7491,44 @@ subroutine define_history_vars(this, initialize_variables) 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', & - use_default='inactive', avgflag='A', vtype=site_age_r8, hlms='CLM:ALM', & + 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_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', & @@ -7915,26 +8132,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', & @@ -8301,6 +8539,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', & @@ -8749,8 +8994,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 diff --git a/main/FatesInterfaceMod.F90 b/main/FatesInterfaceMod.F90 index 27326c8b9d..eaa0a6a3c2 100644 --- a/main/FatesInterfaceMod.F90 +++ b/main/FatesInterfaceMod.F90 @@ -159,7 +159,7 @@ module FatesInterfaceMod ! instance is fine. type(bc_pconst_type) :: bc_pconst - + end type fates_interface_type @@ -373,8 +373,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 @@ -423,6 +426,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 @@ -1493,9 +1500,7 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) hlm_ipedof = unset_int hlm_nu_com = 'unset' hlm_decomp = 'unset' - hlm_nitrogen_spec = unset_int hlm_use_tree_damage = unset_int - hlm_phosphorus_spec = unset_int hlm_use_ch4 = unset_int hlm_use_vertsoilc = unset_int hlm_parteh_mode = unset_int @@ -1505,6 +1510,7 @@ 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 @@ -1697,21 +1703,9 @@ subroutine set_fates_ctrlparms(tag,ival,rval,cval) write(fates_log(),*) 'FATES tree damage (use_fates_tree_damage = .true.) is not' write(fates_log(),*) '(yet) compatible with CNP allocation (fates_parteh_mode = 2)' call endrun(msg=errMsg(sourcefile, __LINE__)) + end if end if - - - end if - - if(hlm_nitrogen_spec .eq. unset_int) then - write(fates_log(),*) 'FATES parameters unset: hlm_nitrogen_spec, exiting' - call endrun(msg=errMsg(sourcefile, __LINE__)) - end if - - if(hlm_phosphorus_spec .eq. unset_int) then - write(fates_log(),*) 'FATES parameters unset: hlm_phosphorus_spec, 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__)) @@ -1773,6 +1767,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 @@ -812,6 +805,15 @@ 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] + ! 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] + 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 @@ -848,10 +850,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 6673f4b819..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,6 +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_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 @@ -210,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 ! ------------------------------------------------------------------------------------------ @@ -231,17 +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 - invsite = & - minloc( (sites(s)%lat-inv_lat_list(:))**2.0_r8 + & - (sites(s)%lon-inv_lon_list(:))**2.0_r8 , 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( (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 ( 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 @@ -481,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/FatesRestartInterfaceMod.F90 b/main/FatesRestartInterfaceMod.F90 index a313dfeaf7..e3e6c579d0 100644 --- a/main/FatesRestartInterfaceMod.F90 +++ b/main/FatesRestartInterfaceMod.F90 @@ -29,7 +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 FatesInterfaceTypesMod, only : hlm_hio_ignore_val use FatesHydraulicsMemMod, only : nshell use FatesHydraulicsMemMod, only : n_hypool_ag use FatesHydraulicsMemMod, only : n_hypool_troot @@ -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,12 @@ 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_herbivory_flux_out_si + integer :: ir_burn_flux_to_atm_si + integer :: ir_ncohort_pa integer :: ir_canopy_layer_co integer :: ir_canopy_layer_yesterday_co @@ -243,9 +248,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 +270,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 +281,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 +317,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 +348,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 +772,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 @@ -1165,6 +1202,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, & @@ -1375,29 +1421,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 +1515,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 +1530,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 +1605,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 +1652,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 +1706,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 +1741,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 +1769,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 +2256,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 +2295,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 +2363,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 +2385,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 +2396,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 +2418,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 +2501,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 +2535,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) @@ -2340,7 +2555,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 @@ -2354,19 +2570,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 @@ -2379,7 +2606,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 +2939,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 +2965,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 +2985,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 +3323,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 +3356,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 +3424,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 +3450,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 +3464,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 +3549,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 +3583,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) @@ -3353,6 +3631,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) @@ -3364,6 +3647,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 +4027,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 +4053,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 +4076,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. @@ -3874,8 +4173,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/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/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_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 4ab4a9126c..e7311ee1a6 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_sci4_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 ; @@ -858,12 +858,6 @@ double fates_recruit_init_nocomp(fates_pft) ; 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" ; @@ -906,6 +900,42 @@ double fates_recruit_init_nocomp(fates_pft) ; 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 = "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 = "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" ; fates_soil_salinity:long_name = "soil salinity used for model when not coupled to dynamic soil salinity" ; @@ -1827,10 +1857,6 @@ fates_mort_bmort = 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 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 ; @@ -1859,6 +1885,30 @@ fates_mort_bmort = 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 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 47d6d95443..eecace30b5 100644 --- a/parteh/PRTAllometricCNPMod.F90 +++ b/parteh/PRTAllometricCNPMod.F90 @@ -52,7 +52,10 @@ module PRTAllometricCNPMod use FatesIntegratorsMod , only : Euler use FatesConstantsMod , only : calloc_abs_error use FatesConstantsMod , only : nearzero - use FatesConstantsMod , only : ievergreen + 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 @@ -60,6 +63,7 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : default_regeneration use FatesConstantsMod , only : TRS_no_seedling_dyn use FatesConstantsMod , only : min_max_dbh_for_trees + use FatesConstantsMod , only : ievergreen use PRTParametersMod , only : prt_params use FatesConstantsMod , only : leaves_on,leaves_off use FatesConstantsMod , only : leaves_shedding @@ -69,7 +73,8 @@ module PRTAllometricCNPMod use FatesConstantsMod , only : prescribed_n_uptake use EDPftvarcon, only : EDPftvarcon_inst use FatesInterfaceTypesMod, only : hlm_regeneration_model - + use FatesInterfaceTypesMod, only : hlm_nitrogen_suppl + use FatesInterfaceTypesMod, only : hlm_phosphorus_suppl implicit none @@ -1845,6 +1850,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 @@ -1898,7 +1905,14 @@ 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 if either nutrient in not being supplemented + limiting_p = ((p_uptake_mode .eq. coupled_p_uptake) .and. (hlm_phosphorus_suppl .eq. ifalse)) + limiting_n = ((n_uptake_mode .eq. coupled_p_uptake) .and. (hlm_nitrogen_suppl .eq. ifalse)) + + if (limiting_p .or. limiting_n) then + call this%CNPAdjustFRootTargets(target_c,target_dcdd) + end if ! ----------------------------------------------------------------------------------- ! If carbon is still available, lets cram some into storage overflow diff --git a/radiation/FatesNormanRadMod.F90 b/radiation/FatesNormanRadMod.F90 index a3ee7522b0..c16635180a 100644 --- a/radiation/FatesNormanRadMod.F90 +++ b/radiation/FatesNormanRadMod.F90 @@ -801,9 +801,11 @@ subroutine PatchNormanRadiation (currentPatch, & ! pass normalized PAR profiles for use in diagnostic averaging for history fields if (ib == ivis) then ! only diagnose PAR profiles for the visible band do iv = 1, currentPatch%nrad(L,ft) - currentPatch%nrmlzd_parprof_pft_dir_z(radtype,L,ft,iv) = & + currentPatch%nrmlzd_parprof_pft_dir_z(L,ft,iv) = & + currentPatch%nrmlzd_parprof_pft_dir_z(L,ft,iv) + & forc_dir(radtype) * tr_dir_z(L,ft,iv) - currentPatch%nrmlzd_parprof_pft_dif_z(radtype,L,ft,iv) = & + currentPatch%nrmlzd_parprof_pft_dif_z(L,ft,iv) = & + currentPatch%nrmlzd_parprof_pft_dif_z(L,ft,iv) + & Dif_dn(L,ft,iv) + Dif_up(L,ft,iv) end do end if ! ib = visible diff --git a/radiation/FatesRadiationDriveMod.F90 b/radiation/FatesRadiationDriveMod.F90 index f595e041be..ff2c5b794e 100644 --- a/radiation/FatesRadiationDriveMod.F90 +++ b/radiation/FatesRadiationDriveMod.F90 @@ -33,7 +33,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 @@ -83,6 +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, 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 !----------------------------------------------------------------------- @@ -125,13 +132,13 @@ 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%rad_error(:) = hlm_hio_ignore_val + 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) - + currentPatch%rad_error(:) = hlm_hio_ignore_val + if_zenith_flag: if( bc_in(s)%coszen>0._r8 )then select case(hlm_radiation_model) @@ -189,8 +196,28 @@ 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 + 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 + end associate end select endif if_zenith_flag @@ -251,8 +278,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,33 +342,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 - if (cpatch%rad_error(ib) /= hlm_hio_ignore_val) then - cpatch%rad_error(ib) = cpatch%rad_error(ib) * & - (bc_in(s)%solad_parb(ifp,ib) + bc_in(s)%solai_parb(ifp,ib)) - else - cpatch%rad_error(ib) = hlm_hio_ignore_val - endif - 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 @@ -394,12 +393,6 @@ subroutine FatesSunShadeFracs(nsites, sites,bc_in,bc_out) vai_bot = dlower_vai(iv+1) end if - 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) @@ -429,10 +422,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) fates_r8 - use FatesUtilsMod, only : QuadraticRootsNSWC, QuadraticRootsSridharachary + use FatesUtilsMod, only : QuadraticRootsNSWC use FatesUtilsMod, only : GetNeighborDistance implicit none 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 7ebb1ae7b9..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 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 + diff --git a/testing/load_functional_tests.py b/testing/load_functional_tests.py index 1c0f0caaef..3b72dcb2b7 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 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 dea91fa894..5f2cdee5fb 100644 --- a/testing/testing_shr/FatesFactoryMod.F90 +++ b/testing/testing_shr/FatesFactoryMod.F90 @@ -90,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) @@ -110,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 @@ -521,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 diff --git a/testing/testing_shr/FatesUnitTestIOMod.F90 b/testing/testing_shr/FatesUnitTestIOMod.F90 index 6b9437a6c4..ef261f1d0f 100644 --- a/testing/testing_shr/FatesUnitTestIOMod.F90 +++ b/testing/testing_shr/FatesUnitTestIOMod.F90 @@ -107,7 +107,7 @@ subroutine Check(status) if (status /= nf90_noerr) then write(*,*) trim(nf90_strerror(status)) - stop + call abort() end if end subroutine Check @@ -135,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 @@ -481,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)) 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/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: 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